blob: b94ad98fbf6a4a764e20cbaf36c0302e2e9df18c [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;
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -080052import android.os.Bundle;
53import android.os.Handler;
54import android.os.RemoteException;
55import android.os.ServiceManager;
Dmitri Plotnikov4e3a0432009-09-15 13:54:40 -070056import android.provider.ContactsContract;
Dmitri Plotnikovb4491ee2009-06-15 09:31:02 -070057import android.provider.ContactsContract.AggregationExceptions;
58import android.provider.ContactsContract.CommonDataKinds;
Evan Millar11d628c2009-09-02 08:55:01 -070059import android.provider.ContactsContract.Contacts;
Evan Millar66388be2009-05-28 15:41:07 -070060import android.provider.ContactsContract.Data;
Daniel Lehmann350ba6f2010-03-23 18:10:57 -070061import android.provider.ContactsContract.DisplayNameSources;
Dmitri Plotnikov39466592009-07-27 11:23:51 -070062import android.provider.ContactsContract.RawContacts;
Fred Quintana8579e252009-12-07 14:50:12 -080063import android.provider.ContactsContract.RawContactsEntity;
Dmitri Plotnikov160e9da2009-09-30 23:48:12 -070064import android.provider.ContactsContract.StatusUpdates;
Jeff Sharkey624ddc32009-10-01 21:32:19 -070065import android.provider.ContactsContract.CommonDataKinds.Email;
66import android.provider.ContactsContract.CommonDataKinds.Im;
67import android.provider.ContactsContract.CommonDataKinds.Nickname;
68import android.provider.ContactsContract.CommonDataKinds.Note;
69import android.provider.ContactsContract.CommonDataKinds.Organization;
Evan Millar54a5c9f2009-06-23 17:41:09 -070070import android.provider.ContactsContract.CommonDataKinds.Phone;
Jeff Sharkey624ddc32009-10-01 21:32:19 -070071import android.provider.ContactsContract.CommonDataKinds.StructuredPostal;
Evan Millar951fa9c2009-10-26 14:57:43 -070072import android.provider.ContactsContract.CommonDataKinds.Website;
Evan Millar54a5c9f2009-06-23 17:41:09 -070073import android.telephony.PhoneNumberUtils;
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -080074import android.text.TextUtils;
75import android.util.Log;
76import android.view.ContextMenu;
77import android.view.KeyEvent;
Evan Millar11d628c2009-09-02 08:55:01 -070078import android.view.LayoutInflater;
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -080079import android.view.Menu;
Jeff Sharkey49d17b32009-09-07 02:14:21 -070080import android.view.MenuInflater;
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -080081import android.view.MenuItem;
82import android.view.View;
83import android.view.ViewGroup;
Evan Millar11d628c2009-09-02 08:55:01 -070084import android.view.Window;
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -080085import android.view.ContextMenu.ContextMenuInfo;
86import android.widget.AdapterView;
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -080087import android.widget.ImageView;
88import android.widget.ListView;
89import android.widget.TextView;
90import android.widget.Toast;
91
92import java.util.ArrayList;
Jeff Sharkey624ddc32009-10-01 21:32:19 -070093import java.util.HashMap;
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -080094
95/**
96 * Displays the details of a specific contact.
97 */
Evan Millar11d628c2009-09-02 08:55:01 -070098public class ViewContactActivity extends Activity
Evan Millar7911ff52009-07-21 15:55:18 -070099 implements View.OnCreateContextMenuListener, DialogInterface.OnClickListener,
Evan Millardf181202009-09-30 15:15:51 -0700100 AdapterView.OnItemClickListener, NotifyingAsyncQueryHandler.AsyncQueryListener {
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800101 private static final String TAG = "ViewContact";
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800102
103 private static final boolean SHOW_SEPARATORS = false;
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800104
105 private static final int DIALOG_CONFIRM_DELETE = 1;
Megha Joshid5afd8a2009-09-22 09:38:41 -0700106 private static final int DIALOG_CONFIRM_READONLY_DELETE = 2;
107 private static final int DIALOG_CONFIRM_MULTIPLE_DELETE = 3;
Megha Joshic508bd82009-10-01 17:31:49 -0700108 private static final int DIALOG_CONFIRM_READONLY_HIDE = 4;
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800109
Evan Millar8a79cee2009-08-19 17:20:49 -0700110 private static final int REQUEST_JOIN_CONTACT = 1;
Evan Millar9cd81242009-09-26 16:02:31 -0700111 private static final int REQUEST_EDIT_CONTACT = 2;
Dmitri Plotnikov49f705f2009-06-17 18:31:56 -0700112
Evan Millar8a79cee2009-08-19 17:20:49 -0700113 public static final int MENU_ITEM_MAKE_DEFAULT = 3;
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800114
Dmitri Plotnikov99eafe72009-09-03 14:09:45 -0700115 protected Uri mLookupUri;
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800116 private ContentResolver mResolver;
117 private ViewAdapter mAdapter;
118 private int mNumPhoneNumbers = 0;
119
Dmitri Plotnikovb4491ee2009-06-15 09:31:02 -0700120 /**
Dmitri Plotnikove1cd6792009-07-27 20:28:17 -0700121 * A list of distinct contact IDs included in the current contact.
Dmitri Plotnikovb4491ee2009-06-15 09:31:02 -0700122 */
Evan Millar7911ff52009-07-21 15:55:18 -0700123 private ArrayList<Long> mRawContactIds = new ArrayList<Long>();
Dmitri Plotnikovb4491ee2009-06-15 09:31:02 -0700124
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800125 /* package */ ArrayList<ViewEntry> mPhoneEntries = new ArrayList<ViewEntry>();
126 /* package */ ArrayList<ViewEntry> mSmsEntries = new ArrayList<ViewEntry>();
127 /* package */ ArrayList<ViewEntry> mEmailEntries = new ArrayList<ViewEntry>();
128 /* package */ ArrayList<ViewEntry> mPostalEntries = new ArrayList<ViewEntry>();
129 /* package */ ArrayList<ViewEntry> mImEntries = new ArrayList<ViewEntry>();
Daniel Lehmanne12f8a92010-02-24 18:37:56 -0800130 /* package */ ArrayList<ViewEntry> mNicknameEntries = new ArrayList<ViewEntry>();
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800131 /* package */ ArrayList<ViewEntry> mOrganizationEntries = new ArrayList<ViewEntry>();
The Android Open Source Projectcac191e2009-03-18 22:20:27 -0700132 /* package */ ArrayList<ViewEntry> mGroupEntries = new ArrayList<ViewEntry>();
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800133 /* package */ ArrayList<ViewEntry> mOtherEntries = new ArrayList<ViewEntry>();
134 /* package */ ArrayList<ArrayList<ViewEntry>> mSections = new ArrayList<ArrayList<ViewEntry>>();
135
136 private Cursor mCursor;
Evan Millar5c22c3b2009-05-29 11:37:54 -0700137
Evan Millar11d628c2009-09-02 08:55:01 -0700138 protected ContactHeaderWidget mContactHeaderWidget;
139 private NotifyingAsyncQueryHandler mHandler;
140
141 protected LayoutInflater mInflater;
142
Megha Joshid5afd8a2009-09-22 09:38:41 -0700143 protected int mReadOnlySourcesCnt;
144 protected int mWritableSourcesCnt;
Jeff Sharkeye0408762009-11-03 12:57:16 -0800145 protected boolean mAllRestricted;
146
Jeff Sharkey827762d2009-12-23 11:06:08 -0800147 protected Uri mPrimaryPhoneUri = null;
148
Megha Joshid5afd8a2009-09-22 09:38:41 -0700149 protected ArrayList<Long> mWritableRawContactIds = new ArrayList<Long>();
Evan Millar11d628c2009-09-02 08:55:01 -0700150
Jeff Sharkey624ddc32009-10-01 21:32:19 -0700151 private static final int TOKEN_ENTITIES = 0;
152 private static final int TOKEN_STATUSES = 1;
153
154 private boolean mHasEntities = false;
155 private boolean mHasStatuses = false;
156
Daniel Lehmann350ba6f2010-03-23 18:10:57 -0700157 private long mNameRawContactId = -1;
158 private int mDisplayNameSource = DisplayNameSources.UNDEFINED;
159
Jeff Sharkey624ddc32009-10-01 21:32:19 -0700160 private ArrayList<Entity> mEntities = Lists.newArrayList();
161 private HashMap<Long, DataStatus> mStatuses = Maps.newHashMap();
Evan Millar11d628c2009-09-02 08:55:01 -0700162
Makoto Onuki7a707382009-11-19 13:35:55 +0900163 /**
164 * The view shown if the detail list is empty.
165 * We set this to the list view when first bind the adapter, so that it won't be shown while
166 * we're loading data.
167 */
168 private View mEmptyView;
169
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800170 private ContentObserver mObserver = new ContentObserver(new Handler()) {
171 @Override
172 public boolean deliverSelfNotifications() {
173 return true;
174 }
175
176 @Override
177 public void onChange(boolean selfChange) {
Evan Millar11d628c2009-09-02 08:55:01 -0700178 if (mCursor != null && !mCursor.isClosed()) {
179 startEntityQuery();
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800180 }
181 }
182 };
183
184 public void onClick(DialogInterface dialog, int which) {
Dmitri Plotnikov99eafe72009-09-03 14:09:45 -0700185 closeCursor();
Megha Joshic508bd82009-10-01 17:31:49 -0700186 getContentResolver().delete(mLookupUri, null, null);
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800187 finish();
188 }
189
Evan Millar7911ff52009-07-21 15:55:18 -0700190 private ListView mListView;
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800191 private boolean mShowSmsLinksForAllPhones;
192
193 @Override
194 protected void onCreate(Bundle icicle) {
195 super.onCreate(icicle);
196
Evan Millar11d628c2009-09-02 08:55:01 -0700197 final Intent intent = getIntent();
Dmitri Plotnikov4e3a0432009-09-15 13:54:40 -0700198 Uri data = intent.getData();
199 String authority = data.getAuthority();
200 if (ContactsContract.AUTHORITY.equals(authority)) {
201 mLookupUri = data;
202 } else if (android.provider.Contacts.AUTHORITY.equals(authority)) {
203 final long rawContactId = ContentUris.parseId(data);
204 mLookupUri = RawContacts.getContactLookupUri(getContentResolver(),
205 ContentUris.withAppendedId(RawContacts.CONTENT_URI, rawContactId));
Dmitri Plotnikov83129f02009-09-02 19:04:41 -0700206
Dmitri Plotnikov4e3a0432009-09-15 13:54:40 -0700207 }
Dmitri Plotnikov83129f02009-09-02 19:04:41 -0700208 mInflater = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE);
Evan Millar11d628c2009-09-02 08:55:01 -0700209
210 requestWindowFeature(Window.FEATURE_NO_TITLE);
211 setContentView(R.layout.contact_card_layout);
212
213 mContactHeaderWidget = (ContactHeaderWidget) findViewById(R.id.contact_header_widget);
214 mContactHeaderWidget.showStar(true);
Evan Millar11d628c2009-09-02 08:55:01 -0700215 mContactHeaderWidget.setExcludeMimes(new String[] {
216 Contacts.CONTENT_ITEM_TYPE
217 });
218
Evan Millar11d628c2009-09-02 08:55:01 -0700219 mHandler = new NotifyingAsyncQueryHandler(this, this);
220
Evan Millarcdeea942009-10-07 11:47:35 -0700221 mListView = (ListView) findViewById(R.id.contact_data);
Evan Millar7911ff52009-07-21 15:55:18 -0700222 mListView.setOnCreateContextMenuListener(this);
223 mListView.setScrollBarStyle(ListView.SCROLLBARS_OUTSIDE_OVERLAY);
224 mListView.setOnItemClickListener(this);
Makoto Onuki7a707382009-11-19 13:35:55 +0900225 // Don't set it to mListView yet. We do so later when we bind the adapter.
226 mEmptyView = findViewById(android.R.id.empty);
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800227
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800228 mResolver = getContentResolver();
229
230 // Build the list of sections. The order they're added to mSections dictates the
231 // order they are displayed in the list.
232 mSections.add(mPhoneEntries);
233 mSections.add(mSmsEntries);
234 mSections.add(mEmailEntries);
235 mSections.add(mImEntries);
236 mSections.add(mPostalEntries);
Daniel Lehmanne12f8a92010-02-24 18:37:56 -0800237 mSections.add(mNicknameEntries);
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800238 mSections.add(mOrganizationEntries);
The Android Open Source Projectcac191e2009-03-18 22:20:27 -0700239 mSections.add(mGroupEntries);
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800240 mSections.add(mOtherEntries);
241
242 //TODO Read this value from a preference
243 mShowSmsLinksForAllPhones = true;
Evan Millar11d628c2009-09-02 08:55:01 -0700244 }
245
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800246 @Override
247 protected void onResume() {
248 super.onResume();
Evan Millar11d628c2009-09-02 08:55:01 -0700249 startEntityQuery();
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800250 }
251
252 @Override
253 protected void onPause() {
254 super.onPause();
Dmitri Plotnikov99eafe72009-09-03 14:09:45 -0700255 closeCursor();
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800256 }
257
258 @Override
259 protected void onDestroy() {
260 super.onDestroy();
Dmitri Plotnikov99eafe72009-09-03 14:09:45 -0700261 closeCursor();
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800262 }
263
264 @Override
265 protected Dialog onCreateDialog(int id) {
266 switch (id) {
267 case DIALOG_CONFIRM_DELETE:
268 return new AlertDialog.Builder(this)
269 .setTitle(R.string.deleteConfirmation_title)
270 .setIcon(android.R.drawable.ic_dialog_alert)
271 .setMessage(R.string.deleteConfirmation)
272 .setNegativeButton(android.R.string.cancel, null)
273 .setPositiveButton(android.R.string.ok, this)
274 .setCancelable(false)
275 .create();
Megha Joshid5afd8a2009-09-22 09:38:41 -0700276 case DIALOG_CONFIRM_READONLY_DELETE:
277 return new AlertDialog.Builder(this)
278 .setTitle(R.string.deleteConfirmation_title)
279 .setIcon(android.R.drawable.ic_dialog_alert)
280 .setMessage(R.string.readOnlyContactDeleteConfirmation)
281 .setNegativeButton(android.R.string.cancel, null)
282 .setPositiveButton(android.R.string.ok, this)
283 .setCancelable(false)
284 .create();
285 case DIALOG_CONFIRM_MULTIPLE_DELETE:
286 return new AlertDialog.Builder(this)
287 .setTitle(R.string.deleteConfirmation_title)
288 .setIcon(android.R.drawable.ic_dialog_alert)
289 .setMessage(R.string.multipleContactDeleteConfirmation)
290 .setNegativeButton(android.R.string.cancel, null)
291 .setPositiveButton(android.R.string.ok, this)
292 .setCancelable(false)
293 .create();
Evan Millarcdeea942009-10-07 11:47:35 -0700294 case DIALOG_CONFIRM_READONLY_HIDE: {
Megha Joshic508bd82009-10-01 17:31:49 -0700295 return new AlertDialog.Builder(this)
296 .setTitle(R.string.deleteConfirmation_title)
297 .setIcon(android.R.drawable.ic_dialog_alert)
298 .setMessage(R.string.readOnlyContactWarning)
299 .setPositiveButton(android.R.string.ok, this)
Evan Millarcdeea942009-10-07 11:47:35 -0700300 .create();
Megha Joshic508bd82009-10-01 17:31:49 -0700301 }
302
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800303 }
304 return null;
305 }
306
Evan Millar11d628c2009-09-02 08:55:01 -0700307 /** {@inheritDoc} */
Fred Quintana8579e252009-12-07 14:50:12 -0800308 public void onQueryComplete(int token, Object cookie, Cursor cursor) {
309 if (token == TOKEN_STATUSES) {
310 try {
311 // Read available social rows and consider binding
312 readStatuses(cursor);
313 } finally {
314 if (cursor != null) {
315 cursor.close();
316 }
317 }
318 } else {
319 EntityIterator iterator = RawContacts.newEntityIterator(cursor);
320 try {
321 // Read incoming entities and consider binding
322 readEntities(iterator);
323 } finally {
Evan Millar11d628c2009-09-02 08:55:01 -0700324 iterator.close();
325 }
326 }
Fred Quintana8579e252009-12-07 14:50:12 -0800327 considerBindData();
Evan Millar11d628c2009-09-02 08:55:01 -0700328 }
329
Evan Millar6a61a1a2009-09-29 14:00:43 -0700330 private long getRefreshedContactId() {
331 Uri freshContactUri = Contacts.lookupContact(getContentResolver(), mLookupUri);
332 if (freshContactUri != null) {
333 return ContentUris.parseId(freshContactUri);
334 }
335 return -1;
336 }
337
Jeff Sharkey624ddc32009-10-01 21:32:19 -0700338 /**
339 * Read from the given {@link EntityIterator} to build internal set of
340 * {@link #mEntities} for data display.
341 */
342 private synchronized void readEntities(EntityIterator iterator) {
343 mEntities.clear();
Evan Millar11d628c2009-09-02 08:55:01 -0700344 try {
345 while (iterator.hasNext()) {
Jeff Sharkey624ddc32009-10-01 21:32:19 -0700346 mEntities.add(iterator.next());
Evan Millar11d628c2009-09-02 08:55:01 -0700347 }
Jeff Sharkey624ddc32009-10-01 21:32:19 -0700348 mHasEntities = true;
Evan Millar11d628c2009-09-02 08:55:01 -0700349 } catch (RemoteException e) {
Jeff Sharkey624ddc32009-10-01 21:32:19 -0700350 Log.w(TAG, "Problem reading contact data: " + e.toString());
Evan Millar11d628c2009-09-02 08:55:01 -0700351 }
Evan Millar11d628c2009-09-02 08:55:01 -0700352 }
353
Jeff Sharkey624ddc32009-10-01 21:32:19 -0700354 /**
355 * Read from the given {@link Cursor} and build a set of {@link DataStatus}
356 * objects to match any valid statuses found.
357 */
358 private synchronized void readStatuses(Cursor cursor) {
359 mStatuses.clear();
360
361 // Walk found statuses, creating internal row for each
362 while (cursor.moveToNext()) {
363 final DataStatus status = new DataStatus(cursor);
364 final long dataId = cursor.getLong(StatusQuery._ID);
365 mStatuses.put(dataId, status);
366 }
367
368 mHasStatuses = true;
369 }
370
371 private synchronized void startEntityQuery() {
Dmitri Plotnikov99eafe72009-09-03 14:09:45 -0700372 closeCursor();
373
Evan Millar6a61a1a2009-09-29 14:00:43 -0700374 Uri uri = null;
Dmitri Plotnikov99eafe72009-09-03 14:09:45 -0700375 if (mLookupUri != null) {
376 mLookupUri = Contacts.getLookupUri(getContentResolver(), mLookupUri);
377 if (mLookupUri != null) {
Evan Millar6a61a1a2009-09-29 14:00:43 -0700378 uri = Contacts.lookupContact(getContentResolver(), mLookupUri);
Dmitri Plotnikov99eafe72009-09-03 14:09:45 -0700379 }
380 }
381
Evan Millar6a61a1a2009-09-29 14:00:43 -0700382 if (uri == null) {
Dmitri Plotnikov99eafe72009-09-03 14:09:45 -0700383
384 // TODO either figure out a way to prevent a flash of black background or
385 // use some other UI than a toast
386 Toast.makeText(this, R.string.invalidContactMessage, Toast.LENGTH_SHORT).show();
387 Log.e(TAG, "invalid contact uri: " + mLookupUri);
388 finish();
389 return;
390 }
391
Jeff Sharkey624ddc32009-10-01 21:32:19 -0700392 final Uri dataUri = Uri.withAppendedPath(uri, Contacts.Data.CONTENT_DIRECTORY);
393
Daniel Lehmann350ba6f2010-03-23 18:10:57 -0700394 // This cursor has two purposes:
395 // - Fetch NAME_RAW_CONTACT_ID and DISPLAY_NAME_SOURCE
396 // - Watcher for change events
Jeff Sharkey624ddc32009-10-01 21:32:19 -0700397 mCursor = mResolver.query(dataUri,
Daniel Lehmann350ba6f2010-03-23 18:10:57 -0700398 new String[] { Contacts.NAME_RAW_CONTACT_ID, Contacts.DISPLAY_NAME_SOURCE },
399 null, null, null);
Dmitri Plotnikov99eafe72009-09-03 14:09:45 -0700400
Daniel Lehmann350ba6f2010-03-23 18:10:57 -0700401 if (mCursor.moveToFirst()) {
402 mNameRawContactId =
403 mCursor.getLong(mCursor.getColumnIndex(Contacts.NAME_RAW_CONTACT_ID));
404 mDisplayNameSource =
405 mCursor.getInt(mCursor.getColumnIndex(Contacts.DISPLAY_NAME_SOURCE));
406 } else {
407 mNameRawContactId = -1;
408 mDisplayNameSource = DisplayNameSources.UNDEFINED;
409 }
410
411 mCursor.registerContentObserver(mObserver);
Jeff Sharkey624ddc32009-10-01 21:32:19 -0700412 final long contactId = ContentUris.parseId(uri);
413
414 // Clear flags and start queries to data and status
415 mHasEntities = false;
416 mHasStatuses = false;
417
Fred Quintana8579e252009-12-07 14:50:12 -0800418 mHandler.startQuery(TOKEN_ENTITIES, null, RawContactsEntity.CONTENT_URI, null,
Jeff Sharkey624ddc32009-10-01 21:32:19 -0700419 RawContacts.CONTACT_ID + "=" + contactId, null, null);
420 mHandler.startQuery(TOKEN_STATUSES, null, dataUri, StatusQuery.PROJECTION,
421 StatusUpdates.PRESENCE + " IS NOT NULL OR " + StatusUpdates.STATUS
422 + " IS NOT NULL", null, null);
Dmitri Plotnikov99eafe72009-09-03 14:09:45 -0700423
Evan Millar9b1a1242009-09-16 10:26:10 -0700424 mContactHeaderWidget.bindFromContactLookupUri(mLookupUri);
Dmitri Plotnikov99eafe72009-09-03 14:09:45 -0700425 }
426
427 private void closeCursor() {
428 if (mCursor != null) {
429 mCursor.unregisterContentObserver(mObserver);
430 mCursor.close();
431 mCursor = null;
432 }
Evan Millar11d628c2009-09-02 08:55:01 -0700433 }
434
Jeff Sharkey624ddc32009-10-01 21:32:19 -0700435 /**
436 * Consider binding views after any of several background queries has
437 * completed. We check internal flags and only bind when all data has
438 * arrived.
439 */
440 private void considerBindData() {
441 if (mHasEntities && mHasStatuses) {
442 bindData();
443 }
444 }
445
Evan Millar11d628c2009-09-02 08:55:01 -0700446 private void bindData() {
447
448 // Build up the contact entries
449 buildEntries();
450
451 // Collapse similar data items in select sections.
452 Collapser.collapseList(mPhoneEntries);
453 Collapser.collapseList(mSmsEntries);
454 Collapser.collapseList(mEmailEntries);
455 Collapser.collapseList(mPostalEntries);
Jeff Sharkey624ddc32009-10-01 21:32:19 -0700456 Collapser.collapseList(mImEntries);
Evan Millar11d628c2009-09-02 08:55:01 -0700457
458 if (mAdapter == null) {
459 mAdapter = new ViewAdapter(this, mSections);
460 mListView.setAdapter(mAdapter);
461 } else {
462 mAdapter.setSections(mSections, SHOW_SEPARATORS);
463 }
Makoto Onuki7a707382009-11-19 13:35:55 +0900464 mListView.setEmptyView(mEmptyView);
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800465 }
466
467 @Override
468 public boolean onCreateOptionsMenu(Menu menu) {
Jeff Sharkey49d17b32009-09-07 02:14:21 -0700469 super.onCreateOptionsMenu(menu);
470
471 final MenuInflater inflater = getMenuInflater();
472 inflater.inflate(R.menu.view, menu);
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800473 return true;
474 }
475
476 @Override
477 public boolean onPrepareOptionsMenu(Menu menu) {
478 super.onPrepareOptionsMenu(menu);
Dmitri Plotnikovb4491ee2009-06-15 09:31:02 -0700479
Evan Millar51514db2009-09-22 11:42:35 -0700480 // Only allow edit when we have at least one raw_contact id
481 final boolean hasRawContact = (mRawContactIds.size() > 0);
Evan Millar2e1b8af2009-09-24 09:39:21 -0700482 menu.findItem(R.id.menu_edit).setEnabled(hasRawContact);
Jeff Sharkey49d17b32009-09-07 02:14:21 -0700483
Jeff Sharkeye0408762009-11-03 12:57:16 -0800484 // Only allow share when unrestricted contacts available
485 menu.findItem(R.id.menu_share).setEnabled(!mAllRestricted);
486
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800487 return true;
488 }
489
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800490 @Override
491 public void onCreateContextMenu(ContextMenu menu, View view, ContextMenuInfo menuInfo) {
492 AdapterView.AdapterContextMenuInfo info;
493 try {
494 info = (AdapterView.AdapterContextMenuInfo) menuInfo;
495 } catch (ClassCastException e) {
496 Log.e(TAG, "bad menuInfo", e);
497 return;
498 }
499
500 // This can be null sometimes, don't crash...
501 if (info == null) {
502 Log.e(TAG, "bad menuInfo");
503 return;
504 }
Evan Millar5c22c3b2009-05-29 11:37:54 -0700505
Evan Millar45e0ed32009-06-01 16:44:38 -0700506 ViewEntry entry = ContactEntryAdapter.getEntry(mSections, info.position, SHOW_SEPARATORS);
Neel Parekha8fb68a2009-09-25 18:05:18 -0700507 menu.setHeaderTitle(R.string.contactOptionsTitle);
Evan Millar45e0ed32009-06-01 16:44:38 -0700508 if (entry.mimetype.equals(CommonDataKinds.Phone.CONTENT_ITEM_TYPE)) {
509 menu.add(0, 0, 0, R.string.menu_call).setIntent(entry.intent);
Evan Millar15e514d2009-08-04 10:14:57 -0700510 menu.add(0, 0, 0, R.string.menu_sendSMS).setIntent(entry.secondaryIntent);
511 if (!entry.isPrimary) {
Evan Millar45e0ed32009-06-01 16:44:38 -0700512 menu.add(0, MENU_ITEM_MAKE_DEFAULT, 0, R.string.menu_makeDefaultNumber);
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800513 }
Evan Millar45e0ed32009-06-01 16:44:38 -0700514 } else if (entry.mimetype.equals(CommonDataKinds.Email.CONTENT_ITEM_TYPE)) {
515 menu.add(0, 0, 0, R.string.menu_sendEmail).setIntent(entry.intent);
Evan Millar15e514d2009-08-04 10:14:57 -0700516 if (!entry.isPrimary) {
Evan Millar45e0ed32009-06-01 16:44:38 -0700517 menu.add(0, MENU_ITEM_MAKE_DEFAULT, 0, R.string.menu_makeDefaultEmail);
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800518 }
Jeff Sharkeyc6ad3ab2009-07-21 19:30:15 -0700519 } else if (entry.mimetype.equals(CommonDataKinds.StructuredPostal.CONTENT_ITEM_TYPE)) {
Evan Millar45e0ed32009-06-01 16:44:38 -0700520 menu.add(0, 0, 0, R.string.menu_viewAddress).setIntent(entry.intent);
521 }
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800522 }
523
524 @Override
525 public boolean onOptionsItemSelected(MenuItem item) {
526 switch (item.getItemId()) {
Jeff Sharkey49d17b32009-09-07 02:14:21 -0700527 case R.id.menu_edit: {
Evan Millardf181202009-09-30 15:15:51 -0700528 Long rawContactIdToEdit = null;
529 if (mRawContactIds.size() > 0) {
530 rawContactIdToEdit = mRawContactIds.get(0);
531 } else {
532 // There is no rawContact to edit.
533 break;
Evan Millardb5d88c2009-08-28 09:31:57 -0700534 }
Evan Millar2e1b8af2009-09-24 09:39:21 -0700535 Uri rawContactUri = ContentUris.withAppendedId(RawContacts.CONTENT_URI,
536 rawContactIdToEdit);
Evan Millar9cd81242009-09-26 16:02:31 -0700537 startActivityForResult(new Intent(Intent.ACTION_EDIT, rawContactUri),
538 REQUEST_EDIT_CONTACT);
Evan Millar8a79cee2009-08-19 17:20:49 -0700539 break;
540 }
Jeff Sharkey49d17b32009-09-07 02:14:21 -0700541 case R.id.menu_delete: {
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800542 // Get confirmation
Evan Millarcdeea942009-10-07 11:47:35 -0700543 if (mReadOnlySourcesCnt > 0 & mWritableSourcesCnt > 0) {
Megha Joshid5afd8a2009-09-22 09:38:41 -0700544 showDialog(DIALOG_CONFIRM_READONLY_DELETE);
Evan Millarcdeea942009-10-07 11:47:35 -0700545 } else if (mReadOnlySourcesCnt > 0 && mWritableSourcesCnt == 0) {
Megha Joshic508bd82009-10-01 17:31:49 -0700546 showDialog(DIALOG_CONFIRM_READONLY_HIDE);
Evan Millarcdeea942009-10-07 11:47:35 -0700547 } else if (mReadOnlySourcesCnt == 0 && mWritableSourcesCnt > 1) {
548 showDialog(DIALOG_CONFIRM_MULTIPLE_DELETE);
549 } else {
550 showDialog(DIALOG_CONFIRM_DELETE);
551 }
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800552 return true;
553 }
Jeff Sharkey49d17b32009-09-07 02:14:21 -0700554 case R.id.menu_join: {
Dmitri Plotnikov49f705f2009-06-17 18:31:56 -0700555 showJoinAggregateActivity();
556 return true;
557 }
Jeff Sharkey49d17b32009-09-07 02:14:21 -0700558 case R.id.menu_options: {
Dmitri Plotnikovef038722009-06-24 18:51:47 -0700559 showOptionsActivity();
560 return true;
561 }
Jeff Sharkey49d17b32009-09-07 02:14:21 -0700562 case R.id.menu_share: {
Jeff Sharkeye0408762009-11-03 12:57:16 -0800563 if (mAllRestricted) return false;
564
Jeff Sharkey93049312009-09-27 17:28:13 -0700565 // TODO: Keep around actual LOOKUP_KEY, or formalize method of extracting
Daniel Lehmanncbf75952010-03-12 13:29:07 -0800566 final String lookupKey = Uri.encode(mLookupUri.getPathSegments().get(2));
Jeff Sharkey93049312009-09-27 17:28:13 -0700567 final Uri shareUri = Uri.withAppendedPath(Contacts.CONTENT_VCARD_URI, lookupKey);
568
Jeff Sharkey49d17b32009-09-07 02:14:21 -0700569 final Intent intent = new Intent(Intent.ACTION_SEND);
Jeff Sharkey93049312009-09-27 17:28:13 -0700570 intent.setType(Contacts.CONTENT_VCARD_TYPE);
571 intent.putExtra(Intent.EXTRA_STREAM, shareUri);
Dmitri Plotnikovef038722009-06-24 18:51:47 -0700572
Jeff Sharkey49d17b32009-09-07 02:14:21 -0700573 // Launch chooser to share contact via
574 final CharSequence chooseTitle = getText(R.string.share_via);
575 final Intent chooseIntent = Intent.createChooser(intent, chooseTitle);
Evan Millar5c22c3b2009-05-29 11:37:54 -0700576
Jeff Sharkey49d17b32009-09-07 02:14:21 -0700577 try {
578 startActivity(chooseIntent);
579 } catch (ActivityNotFoundException ex) {
580 Toast.makeText(this, R.string.share_error, Toast.LENGTH_SHORT).show();
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800581 }
Jeff Sharkey49d17b32009-09-07 02:14:21 -0700582 return true;
583 }
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800584 }
585 return super.onOptionsItemSelected(item);
586 }
Evan Millar5c22c3b2009-05-29 11:37:54 -0700587
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800588 @Override
589 public boolean onContextItemSelected(MenuItem item) {
590 switch (item.getItemId()) {
591 case MENU_ITEM_MAKE_DEFAULT: {
Dmitri Plotnikovb4491ee2009-06-15 09:31:02 -0700592 if (makeItemDefault(item)) {
593 return true;
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800594 }
Dmitri Plotnikovb4491ee2009-06-15 09:31:02 -0700595 break;
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800596 }
597 }
Dmitri Plotnikovb4491ee2009-06-15 09:31:02 -0700598
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800599 return super.onContextItemSelected(item);
600 }
601
Dmitri Plotnikovb4491ee2009-06-15 09:31:02 -0700602 private boolean makeItemDefault(MenuItem item) {
603 ViewEntry entry = getViewEntryForMenuItem(item);
604 if (entry == null) {
605 return false;
606 }
607
608 // Update the primary values in the data record.
Evan Millar14fecb62009-09-09 09:23:12 -0700609 ContentValues values = new ContentValues(1);
Evan Millar54a5c9f2009-06-23 17:41:09 -0700610 values.put(Data.IS_SUPER_PRIMARY, 1);
Dmitri Plotnikovb4491ee2009-06-15 09:31:02 -0700611 getContentResolver().update(ContentUris.withAppendedId(Data.CONTENT_URI, entry.id),
612 values, null, null);
Evan Millar11d628c2009-09-02 08:55:01 -0700613 startEntityQuery();
Dmitri Plotnikovb4491ee2009-06-15 09:31:02 -0700614 return true;
615 }
616
617 /**
Dmitri Plotnikov49f705f2009-06-17 18:31:56 -0700618 * Shows a list of aggregates that can be joined into the currently viewed aggregate.
Dmitri Plotnikovb4491ee2009-06-15 09:31:02 -0700619 */
Dmitri Plotnikov49f705f2009-06-17 18:31:56 -0700620 public void showJoinAggregateActivity() {
Evan Millar6a61a1a2009-09-29 14:00:43 -0700621 long freshId = getRefreshedContactId();
622 if (freshId > 0) {
623 String displayName = null;
624 if (mCursor.moveToFirst()) {
625 displayName = mCursor.getString(0);
626 }
627 Intent intent = new Intent(ContactsListActivity.JOIN_AGGREGATE);
628 intent.putExtra(ContactsListActivity.EXTRA_AGGREGATE_ID, freshId);
629 if (displayName != null) {
630 intent.putExtra(ContactsListActivity.EXTRA_AGGREGATE_NAME, displayName);
631 }
632 startActivityForResult(intent, REQUEST_JOIN_CONTACT);
Dmitri Plotnikova5cfca32009-09-29 18:13:08 -0700633 }
Dmitri Plotnikov49f705f2009-06-17 18:31:56 -0700634 }
Dmitri Plotnikovb4491ee2009-06-15 09:31:02 -0700635
Dmitri Plotnikov49f705f2009-06-17 18:31:56 -0700636 @Override
637 protected void onActivityResult(int requestCode, int resultCode, Intent intent) {
Evan Millar2e1b8af2009-09-24 09:39:21 -0700638 if (requestCode == REQUEST_JOIN_CONTACT) {
639 if (resultCode == RESULT_OK && intent != null) {
640 final long contactId = ContentUris.parseId(intent.getData());
641 joinAggregate(contactId);
Evan Millar8a79cee2009-08-19 17:20:49 -0700642 }
Evan Millar9cd81242009-09-26 16:02:31 -0700643 } else if (requestCode == REQUEST_EDIT_CONTACT) {
Evan Millardf181202009-09-30 15:15:51 -0700644 if (resultCode == EditContactActivity.RESULT_CLOSE_VIEW_ACTIVITY) {
645 finish();
Jeff Hamilton5297c6a2009-10-01 02:22:33 -0700646 } else if (resultCode == Activity.RESULT_OK) {
Dmitri Plotnikov160e9da2009-09-30 23:48:12 -0700647 mLookupUri = intent.getData();
648 if (mLookupUri == null) {
649 finish();
650 }
Evan Millardf181202009-09-30 15:15:51 -0700651 }
Dmitri Plotnikov49f705f2009-06-17 18:31:56 -0700652 }
653 }
654
Dmitri Plotnikovf40ccdf2009-09-04 17:37:15 -0700655 private void joinAggregate(final long contactId) {
Dmitri Plotnikov39466592009-07-27 11:23:51 -0700656 Cursor c = mResolver.query(RawContacts.CONTENT_URI, new String[] {RawContacts._ID},
Dmitri Plotnikovf40ccdf2009-09-04 17:37:15 -0700657 RawContacts.CONTACT_ID + "=" + contactId, null, null);
Dmitri Plotnikov49f705f2009-06-17 18:31:56 -0700658
659 try {
660 while(c.moveToNext()) {
Dmitri Plotnikovf40ccdf2009-09-04 17:37:15 -0700661 long rawContactId = c.getLong(0);
662 setAggregationException(rawContactId, AggregationExceptions.TYPE_KEEP_TOGETHER);
Dmitri Plotnikov49f705f2009-06-17 18:31:56 -0700663 }
664 } finally {
665 c.close();
666 }
667
Dmitri Plotnikov040dc152009-09-03 15:17:56 -0700668 Toast.makeText(this, R.string.contactsJoinedMessage, Toast.LENGTH_LONG).show();
669 startEntityQuery();
Dmitri Plotnikov49f705f2009-06-17 18:31:56 -0700670 }
671
672 /**
673 * Given a contact ID sets an aggregation exception to either join the contact with the
674 * current aggregate or split off.
675 */
Dmitri Plotnikov99eafe72009-09-03 14:09:45 -0700676 protected void setAggregationException(long rawContactId, int exceptionType) {
Dmitri Plotnikovd09f75c2009-06-16 11:59:22 -0700677 ContentValues values = new ContentValues(3);
Dmitri Plotnikovf40ccdf2009-09-04 17:37:15 -0700678 for (long aRawContactId : mRawContactIds) {
679 if (aRawContactId != rawContactId) {
680 values.put(AggregationExceptions.RAW_CONTACT_ID1, aRawContactId);
681 values.put(AggregationExceptions.RAW_CONTACT_ID2, rawContactId);
682 values.put(AggregationExceptions.TYPE, exceptionType);
683 mResolver.update(AggregationExceptions.CONTENT_URI, values, null, null);
684 }
685 }
Dmitri Plotnikovb4491ee2009-06-15 09:31:02 -0700686 }
687
Dmitri Plotnikovef038722009-06-24 18:51:47 -0700688 private void showOptionsActivity() {
689 final Intent intent = new Intent(this, ContactOptionsActivity.class);
Evan Millar6a61a1a2009-09-29 14:00:43 -0700690 intent.setData(mLookupUri);
Dmitri Plotnikovef038722009-06-24 18:51:47 -0700691 startActivity(intent);
692 }
693
Dmitri Plotnikovb4491ee2009-06-15 09:31:02 -0700694 private ViewEntry getViewEntryForMenuItem(MenuItem item) {
695 AdapterView.AdapterContextMenuInfo info;
696 try {
697 info = (AdapterView.AdapterContextMenuInfo) item.getMenuInfo();
698 } catch (ClassCastException e) {
699 Log.e(TAG, "bad menuInfo", e);
700 return null;
701 }
702
703 return ContactEntryAdapter.getEntry(mSections, info.position, SHOW_SEPARATORS);
704 }
705
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800706 @Override
707 public boolean onKeyDown(int keyCode, KeyEvent event) {
708 switch (keyCode) {
709 case KeyEvent.KEYCODE_CALL: {
710 try {
711 ITelephony phone = ITelephony.Stub.asInterface(
712 ServiceManager.checkService("phone"));
713 if (phone != null && !phone.isIdle()) {
714 // Skip out and let the key be handled at a higher level
715 break;
716 }
717 } catch (RemoteException re) {
718 // Fall through and try to call the contact
719 }
720
Evan Millar7911ff52009-07-21 15:55:18 -0700721 int index = mListView.getSelectedItemPosition();
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800722 if (index != -1) {
723 ViewEntry entry = ViewAdapter.getEntry(mSections, index, SHOW_SEPARATORS);
Bai Tao67205ed2010-03-16 08:09:11 +0800724 if (entry != null &&
725 entry.intent.getAction() == Intent.ACTION_CALL_PRIVILEGED) {
Evan Millar66388be2009-05-28 15:41:07 -0700726 startActivity(entry.intent);
Bai Tao67205ed2010-03-16 08:09:11 +0800727 return true;
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800728 }
Jeff Sharkey827762d2009-12-23 11:06:08 -0800729 } else if (mPrimaryPhoneUri != null) {
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800730 // There isn't anything selected, call the default number
Jeff Sharkey827762d2009-12-23 11:06:08 -0800731 final Intent intent = new Intent(Intent.ACTION_CALL_PRIVILEGED,
732 mPrimaryPhoneUri);
733 startActivity(intent);
Bai Tao67205ed2010-03-16 08:09:11 +0800734 return true;
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800735 }
Bai Tao67205ed2010-03-16 08:09:11 +0800736 return false;
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800737 }
738
739 case KeyEvent.KEYCODE_DEL: {
Evan Millarcdeea942009-10-07 11:47:35 -0700740 if (mReadOnlySourcesCnt > 0 & mWritableSourcesCnt > 0) {
Megha Joshid5afd8a2009-09-22 09:38:41 -0700741 showDialog(DIALOG_CONFIRM_READONLY_DELETE);
Evan Millarcdeea942009-10-07 11:47:35 -0700742 } else if (mReadOnlySourcesCnt > 0 && mWritableSourcesCnt == 0) {
Megha Joshic508bd82009-10-01 17:31:49 -0700743 showDialog(DIALOG_CONFIRM_READONLY_HIDE);
Evan Millarcdeea942009-10-07 11:47:35 -0700744 } else if (mReadOnlySourcesCnt == 0 && mWritableSourcesCnt > 1) {
745 showDialog(DIALOG_CONFIRM_MULTIPLE_DELETE);
746 } else {
747 showDialog(DIALOG_CONFIRM_DELETE);
748 }
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800749 return true;
750 }
751 }
752
753 return super.onKeyDown(keyCode, event);
754 }
755
Evan Millar7911ff52009-07-21 15:55:18 -0700756 public void onItemClick(AdapterView parent, View v, int position, long id) {
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800757 ViewEntry entry = ViewAdapter.getEntry(mSections, position, SHOW_SEPARATORS);
758 if (entry != null) {
759 Intent intent = entry.intent;
Evan Millardf181202009-09-30 15:15:51 -0700760 if (intent != null) {
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800761 try {
762 startActivity(intent);
763 } catch (ActivityNotFoundException e) {
764 Log.e(TAG, "No activity found for intent: " + intent);
765 signalError();
766 }
767 } else {
768 signalError();
769 }
770 } else {
771 signalError();
772 }
773 }
774
775 /**
776 * Signal an error to the user via a beep, or some other method.
777 */
778 private void signalError() {
779 //TODO: implement this when we have the sonification APIs
780 }
781
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800782 /**
783 * Build up the entries to display on the screen.
Evan Millar5c22c3b2009-05-29 11:37:54 -0700784 *
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800785 * @param personCursor the URI for the contact being displayed
786 */
Evan Millar11d628c2009-09-02 08:55:01 -0700787 private final void buildEntries() {
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800788 // Clear out the old entries
789 final int numSections = mSections.size();
790 for (int i = 0; i < numSections; i++) {
791 mSections.get(i).clear();
792 }
793
Evan Millar7911ff52009-07-21 15:55:18 -0700794 mRawContactIds.clear();
Jeff Sharkeye0408762009-11-03 12:57:16 -0800795
Megha Joshid5afd8a2009-09-22 09:38:41 -0700796 mReadOnlySourcesCnt = 0;
Jeff Sharkey624ddc32009-10-01 21:32:19 -0700797 mWritableSourcesCnt = 0;
Jeff Sharkeye0408762009-11-03 12:57:16 -0800798 mAllRestricted = true;
Jeff Sharkey827762d2009-12-23 11:06:08 -0800799 mPrimaryPhoneUri = null;
Jeff Sharkeye0408762009-11-03 12:57:16 -0800800
Megha Joshid5afd8a2009-09-22 09:38:41 -0700801 mWritableRawContactIds.clear();
Dmitri Plotnikovb4491ee2009-06-15 09:31:02 -0700802
Jeff Sharkey624ddc32009-10-01 21:32:19 -0700803 final Context context = this;
804 final Sources sources = Sources.getInstance(context);
Evan Millar11d628c2009-09-02 08:55:01 -0700805
Evan Millar66388be2009-05-28 15:41:07 -0700806 // Build up method entries
Evan Millar6a61a1a2009-09-29 14:00:43 -0700807 if (mLookupUri != null) {
Evan Millar11d628c2009-09-02 08:55:01 -0700808 for (Entity entity: mEntities) {
809 final ContentValues entValues = entity.getEntityValues();
810 final String accountType = entValues.getAsString(RawContacts.ACCOUNT_TYPE);
Jeff Sharkeyab066932009-09-21 09:55:30 -0700811 final long rawContactId = entValues.getAsLong(RawContacts._ID);
Evan Millar7911ff52009-07-21 15:55:18 -0700812
Jeff Sharkeye0408762009-11-03 12:57:16 -0800813 // Mark when this contact has any unrestricted components
814 final boolean isRestricted = entValues.getAsInteger(RawContacts.IS_RESTRICTED) != 0;
815 if (!isRestricted) mAllRestricted = false;
816
Dmitri Plotnikov2a8cef02009-09-23 19:01:15 -0700817 if (!mRawContactIds.contains(rawContactId)) {
818 mRawContactIds.add(rawContactId);
819 }
Megha Joshic508bd82009-10-01 17:31:49 -0700820 ContactsSource contactsSource = sources.getInflatedSource(accountType,
821 ContactsSource.LEVEL_SUMMARY);
822 if (contactsSource != null && contactsSource.readOnly) {
823 mReadOnlySourcesCnt += 1;
824 } else {
825 mWritableSourcesCnt += 1;
Evan Millarcdeea942009-10-07 11:47:35 -0700826 mWritableRawContactIds.add(rawContactId);
827 }
Megha Joshic508bd82009-10-01 17:31:49 -0700828
Dmitri Plotnikov2a8cef02009-09-23 19:01:15 -0700829
Evan Millar11d628c2009-09-02 08:55:01 -0700830 for (NamedContentValues subValue : entity.getSubValues()) {
Jeff Sharkeyab066932009-09-21 09:55:30 -0700831 final ContentValues entryValues = subValue.values;
832 entryValues.put(Data.RAW_CONTACT_ID, rawContactId);
833
Jeff Sharkey624ddc32009-10-01 21:32:19 -0700834 final long dataId = entryValues.getAsLong(Data._ID);
835 final String mimeType = entryValues.getAsString(Data.MIMETYPE);
836 if (mimeType == null) continue;
Evan Millar66388be2009-05-28 15:41:07 -0700837
Jeff Sharkey624ddc32009-10-01 21:32:19 -0700838 final DataKind kind = sources.getKindOrFallback(accountType, mimeType, this,
Evan Millar11d628c2009-09-02 08:55:01 -0700839 ContactsSource.LEVEL_MIMETYPES);
Jeff Sharkeyab066932009-09-21 09:55:30 -0700840 if (kind == null) continue;
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800841
Jeff Sharkey624ddc32009-10-01 21:32:19 -0700842 final ViewEntry entry = ViewEntry.fromValues(context, mimeType, kind,
843 rawContactId, dataId, entryValues);
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800844
Jeff Sharkey624ddc32009-10-01 21:32:19 -0700845 final boolean hasData = !TextUtils.isEmpty(entry.data);
Evan Millarff04a272009-09-26 15:23:37 -0700846 final boolean isSuperPrimary = entryValues.getAsInteger(
847 Data.IS_SUPER_PRIMARY) != 0;
Evan Millar11d628c2009-09-02 08:55:01 -0700848
Jeff Sharkey624ddc32009-10-01 21:32:19 -0700849 if (Phone.CONTENT_ITEM_TYPE.equals(mimeType) && hasData) {
Evan Millarff04a272009-09-26 15:23:37 -0700850 // Build phone entries
851 mNumPhoneNumbers++;
Evan Millar11d628c2009-09-02 08:55:01 -0700852
Evan Millarff04a272009-09-26 15:23:37 -0700853 entry.intent = new Intent(Intent.ACTION_CALL_PRIVILEGED,
Jeff Sharkey624ddc32009-10-01 21:32:19 -0700854 Uri.fromParts(Constants.SCHEME_TEL, entry.data, null));
Evan Millarff04a272009-09-26 15:23:37 -0700855 entry.secondaryIntent = new Intent(Intent.ACTION_SENDTO,
Jeff Sharkey624ddc32009-10-01 21:32:19 -0700856 Uri.fromParts(Constants.SCHEME_SMSTO, entry.data, null));
Evan Millar49714ee2009-09-02 16:42:47 -0700857
Jeff Sharkey827762d2009-12-23 11:06:08 -0800858 // Remember super-primary phone
859 if (isSuperPrimary) mPrimaryPhoneUri = entry.uri;
860
Evan Millarff04a272009-09-26 15:23:37 -0700861 entry.isPrimary = isSuperPrimary;
862 mPhoneEntries.add(entry);
863
864 if (entry.type == CommonDataKinds.Phone.TYPE_MOBILE
865 || mShowSmsLinksForAllPhones) {
866 // Add an SMS entry
867 if (kind.iconAltRes > 0) {
868 entry.secondaryActionIcon = kind.iconAltRes;
Evan Millar49714ee2009-09-02 16:42:47 -0700869 }
Evan Millarff04a272009-09-26 15:23:37 -0700870 }
Jeff Sharkey624ddc32009-10-01 21:32:19 -0700871 } else if (Email.CONTENT_ITEM_TYPE.equals(mimeType) && hasData) {
Evan Millarff04a272009-09-26 15:23:37 -0700872 // Build email entries
873 entry.intent = new Intent(Intent.ACTION_SENDTO,
Jeff Sharkey624ddc32009-10-01 21:32:19 -0700874 Uri.fromParts(Constants.SCHEME_MAILTO, entry.data, null));
Evan Millarff04a272009-09-26 15:23:37 -0700875 entry.isPrimary = isSuperPrimary;
876 mEmailEntries.add(entry);
Jeff Sharkey624ddc32009-10-01 21:32:19 -0700877
878 // When Email rows have status, create additional Im row
879 final DataStatus status = mStatuses.get(entry.id);
880 if (status != null) {
881 final String imMime = Im.CONTENT_ITEM_TYPE;
882 final DataKind imKind = sources.getKindOrFallback(accountType,
883 imMime, this, ContactsSource.LEVEL_MIMETYPES);
884 final ViewEntry imEntry = ViewEntry.fromValues(context,
885 imMime, imKind, rawContactId, dataId, entryValues);
886 imEntry.intent = ContactsUtils.buildImIntent(entryValues);
887 imEntry.applyStatus(status, false);
888 mImEntries.add(imEntry);
889 }
890 } else if (StructuredPostal.CONTENT_ITEM_TYPE.equals(mimeType) && hasData) {
Evan Millarff04a272009-09-26 15:23:37 -0700891 // Build postal entries
892 entry.maxLines = 4;
Jeff Sharkey624ddc32009-10-01 21:32:19 -0700893 entry.intent = new Intent(Intent.ACTION_VIEW, entry.uri);
Evan Millarff04a272009-09-26 15:23:37 -0700894 mPostalEntries.add(entry);
Jeff Sharkey624ddc32009-10-01 21:32:19 -0700895 } else if (Im.CONTENT_ITEM_TYPE.equals(mimeType) && hasData) {
896 // Build IM entries
897 entry.intent = ContactsUtils.buildImIntent(entryValues);
Evan Millarff04a272009-09-26 15:23:37 -0700898 if (TextUtils.isEmpty(entry.label)) {
899 entry.label = getString(R.string.chat).toLowerCase();
900 }
Evan Millar11d628c2009-09-02 08:55:01 -0700901
Jeff Sharkey624ddc32009-10-01 21:32:19 -0700902 // Apply presence and status details when available
903 final DataStatus status = mStatuses.get(entry.id);
904 if (status != null) {
905 entry.applyStatus(status, false);
Evan Millarff04a272009-09-26 15:23:37 -0700906 }
Evan Millarff04a272009-09-26 15:23:37 -0700907 mImEntries.add(entry);
Daniel Lehmanne12f8a92010-02-24 18:37:56 -0800908 } else if (Organization.CONTENT_ITEM_TYPE.equals(mimeType) &&
909 (hasData || !TextUtils.isEmpty(entry.label))) {
910 // Build organization entries
Daniel Lehmann350ba6f2010-03-23 18:10:57 -0700911 final boolean isNameRawContact = (mNameRawContactId == rawContactId);
912
913 final boolean duplicatesTitle =
914 isNameRawContact
915 && mDisplayNameSource == DisplayNameSources.ORGANIZATION
916 && !hasData;
917
918 if (!duplicatesTitle) {
919 entry.uri = null;
920 mOrganizationEntries.add(entry);
921 }
Daniel Lehmanne12f8a92010-02-24 18:37:56 -0800922 } else if (Nickname.CONTENT_ITEM_TYPE.equals(mimeType) && hasData) {
923 // Build nickname entries
Daniel Lehmann350ba6f2010-03-23 18:10:57 -0700924 final boolean isNameRawContact = (mNameRawContactId == rawContactId);
925
926 final boolean duplicatesTitle =
927 isNameRawContact
928 && mDisplayNameSource == DisplayNameSources.NICKNAME;
929
930 if (!duplicatesTitle) {
931 entry.uri = null;
932 mNicknameEntries.add(entry);
933 }
Jeff Sharkey624ddc32009-10-01 21:32:19 -0700934 } else if (Note.CONTENT_ITEM_TYPE.equals(mimeType) && hasData) {
Evan Millar11d628c2009-09-02 08:55:01 -0700935 // Build note entries
Evan Millar11d628c2009-09-02 08:55:01 -0700936 entry.uri = null;
Daniel Lehmann0e322012010-02-25 16:09:46 -0800937 entry.maxLines = 100;
Evan Millar11d628c2009-09-02 08:55:01 -0700938 mOtherEntries.add(entry);
Evan Millar951fa9c2009-10-26 14:57:43 -0700939 } else if (Website.CONTENT_ITEM_TYPE.equals(mimeType) && hasData) {
940 // Build note entries
941 entry.uri = null;
942 entry.maxLines = 10;
943 try {
944 WebAddress webAddress = new WebAddress(entry.data);
Makoto Onuki7a707382009-11-19 13:35:55 +0900945 entry.intent = new Intent(Intent.ACTION_VIEW,
Evan Millar951fa9c2009-10-26 14:57:43 -0700946 Uri.parse(webAddress.toString()));
947 } catch (ParseException e) {
948 Log.e(TAG, "Couldn't parse website: " + entry.data);
949 }
950 mOtherEntries.add(entry);
Jeff Sharkeyab066932009-09-21 09:55:30 -0700951 } else {
Jeff Sharkey624ddc32009-10-01 21:32:19 -0700952 // Handle showing custom rows
Evan Millarff04a272009-09-26 15:23:37 -0700953 entry.intent = new Intent(Intent.ACTION_VIEW, entry.uri);
Jeff Sharkey624ddc32009-10-01 21:32:19 -0700954
955 // Use social summary when requested by external source
956 final DataStatus status = mStatuses.get(entry.id);
957 final boolean hasSocial = kind.actionBodySocial && status != null;
958 if (hasSocial) {
959 entry.applyStatus(status, true);
960 }
961
962 if (hasSocial || hasData) {
963 mOtherEntries.add(entry);
964 }
Evan Millar11d628c2009-09-02 08:55:01 -0700965 }
Evan Millar2e1b8af2009-09-24 09:39:21 -0700966 }
Evan Millar66388be2009-05-28 15:41:07 -0700967 }
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800968 }
969 }
970
Jeff Sharkey624ddc32009-10-01 21:32:19 -0700971 static String buildActionString(DataKind kind, ContentValues values, boolean lowerCase,
972 Context context) {
Evan Millar11d628c2009-09-02 08:55:01 -0700973 if (kind.actionHeader == null) {
974 return null;
Jeff Hamilton8350e5b2009-03-24 21:01:34 -0700975 }
Jeff Sharkey624ddc32009-10-01 21:32:19 -0700976 CharSequence actionHeader = kind.actionHeader.inflateUsing(context, values);
Evan Millar11d628c2009-09-02 08:55:01 -0700977 if (actionHeader == null) {
978 return null;
979 }
980 return lowerCase ? actionHeader.toString().toLowerCase() : actionHeader.toString();
981 }
Jeff Hamilton8350e5b2009-03-24 21:01:34 -0700982
Jeff Sharkey624ddc32009-10-01 21:32:19 -0700983 static String buildDataString(DataKind kind, ContentValues values, Context context) {
Evan Millar11d628c2009-09-02 08:55:01 -0700984 if (kind.actionBody == null) {
985 return null;
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800986 }
Jeff Sharkey624ddc32009-10-01 21:32:19 -0700987 CharSequence actionBody = kind.actionBody.inflateUsing(context, values);
Evan Millar11d628c2009-09-02 08:55:01 -0700988 return actionBody == null ? null : actionBody.toString();
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800989 }
Evan Millar5c22c3b2009-05-29 11:37:54 -0700990
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800991 /**
992 * A basic structure with the data for a contact entry in the list.
993 */
Jeff Sharkey624ddc32009-10-01 21:32:19 -0700994 static class ViewEntry extends ContactEntryAdapter.Entry implements Collapsible<ViewEntry> {
995 public Context context = null;
Jeff Sharkeyab066932009-09-21 09:55:30 -0700996 public String resPackageName = null;
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800997 public int actionIcon = -1;
Evan Millar15e514d2009-08-04 10:14:57 -0700998 public boolean isPrimary = false;
Evan Millar15e514d2009-08-04 10:14:57 -0700999 public int secondaryActionIcon = -1;
1000 public Intent intent;
1001 public Intent secondaryIntent = null;
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08001002 public int maxLabelLines = 1;
Evan Millar54a5c9f2009-06-23 17:41:09 -07001003 public ArrayList<Long> ids = new ArrayList<Long>();
1004 public int collapseCount = 0;
1005
Jeff Sharkey624ddc32009-10-01 21:32:19 -07001006 public int presence = -1;
Jeff Sharkey624ddc32009-10-01 21:32:19 -07001007
1008 public CharSequence footerLine = null;
1009
1010 private ViewEntry() {
1011 }
1012
1013 /**
1014 * Build new {@link ViewEntry} and populate from the given values.
1015 */
1016 public static ViewEntry fromValues(Context context, String mimeType, DataKind kind,
1017 long rawContactId, long dataId, ContentValues values) {
1018 final ViewEntry entry = new ViewEntry();
1019 entry.context = context;
1020 entry.contactId = rawContactId;
1021 entry.id = dataId;
1022 entry.uri = ContentUris.withAppendedId(Data.CONTENT_URI, entry.id);
1023 entry.mimetype = mimeType;
1024 entry.label = buildActionString(kind, values, false, context);
1025 entry.data = buildDataString(kind, values, context);
1026
1027 if (kind.typeColumn != null && values.containsKey(kind.typeColumn)) {
1028 entry.type = values.getAsInteger(kind.typeColumn);
1029 }
1030 if (kind.iconRes > 0) {
1031 entry.resPackageName = kind.resPackageName;
1032 entry.actionIcon = kind.iconRes;
1033 }
1034
1035 return entry;
1036 }
1037
1038 /**
1039 * Apply given {@link DataStatus} values over this {@link ViewEntry}
1040 *
1041 * @param fillData When true, the given status replaces {@link #data}
1042 * and {@link #footerLine}. Otherwise only {@link #presence}
1043 * is updated.
1044 */
1045 public ViewEntry applyStatus(DataStatus status, boolean fillData) {
1046 presence = status.getPresence();
Jeff Sharkey624ddc32009-10-01 21:32:19 -07001047 if (fillData && status.isValid()) {
1048 this.data = status.getStatus().toString();
1049 this.footerLine = status.getTimestampLabel(context);
1050 }
1051
1052 return this;
1053 }
1054
Evan Millar54a5c9f2009-06-23 17:41:09 -07001055 public boolean collapseWith(ViewEntry entry) {
1056 // assert equal collapse keys
Evan Millaradb0f8c2009-09-28 17:20:50 -07001057 if (!shouldCollapseWith(entry)) {
Evan Millar54a5c9f2009-06-23 17:41:09 -07001058 return false;
1059 }
1060
1061 // Choose the label associated with the highest type precedence.
1062 if (TypePrecedence.getTypePrecedence(mimetype, type)
1063 > TypePrecedence.getTypePrecedence(entry.mimetype, entry.type)) {
1064 type = entry.type;
1065 label = entry.label;
1066 }
1067
1068 // Choose the max of the maxLines and maxLabelLines values.
1069 maxLines = Math.max(maxLines, entry.maxLines);
1070 maxLabelLines = Math.max(maxLabelLines, entry.maxLabelLines);
1071
1072 // Choose the presence with the highest precedence.
Jeff Sharkey624ddc32009-10-01 21:32:19 -07001073 if (StatusUpdates.getPresencePrecedence(presence)
1074 < StatusUpdates.getPresencePrecedence(entry.presence)) {
1075 presence = entry.presence;
Evan Millar54a5c9f2009-06-23 17:41:09 -07001076 }
1077
1078 // If any of the collapsed entries are primary make the whole thing primary.
Evan Millar15e514d2009-08-04 10:14:57 -07001079 isPrimary = entry.isPrimary ? true : isPrimary;
Evan Millar54a5c9f2009-06-23 17:41:09 -07001080
1081 // uri, and contactdId, shouldn't make a difference. Just keep the original.
1082
1083 // Keep track of all the ids that have been collapsed with this one.
1084 ids.add(entry.id);
1085 collapseCount++;
1086 return true;
1087 }
1088
Evan Millaradb0f8c2009-09-28 17:20:50 -07001089 public boolean shouldCollapseWith(ViewEntry entry) {
1090 if (entry == null) {
1091 return false;
1092 }
1093
Makoto Onukic710b0e2009-10-13 15:43:24 -07001094 if (!ContactsUtils.areDataEqual(context, mimetype, data, entry.mimetype, entry.data)) {
1095 return false;
Evan Millaradb0f8c2009-09-28 17:20:50 -07001096 }
1097
Makoto Onukic710b0e2009-10-13 15:43:24 -07001098 if (!TextUtils.equals(mimetype, entry.mimetype)
1099 || !ContactsUtils.areIntentActionEqual(intent, entry.intent)
1100 || !ContactsUtils.areIntentActionEqual(secondaryIntent, entry.secondaryIntent)
Evan Millaradb0f8c2009-09-28 17:20:50 -07001101 || actionIcon != entry.actionIcon) {
1102 return false;
1103 }
1104
1105 return true;
1106 }
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08001107 }
1108
Evan Millar15e514d2009-08-04 10:14:57 -07001109 /** Cache of the children views of a row */
1110 static class ViewCache {
1111 public TextView label;
1112 public TextView data;
Jeff Sharkey624ddc32009-10-01 21:32:19 -07001113 public TextView footer;
Evan Millar15e514d2009-08-04 10:14:57 -07001114 public ImageView actionIcon;
1115 public ImageView presenceIcon;
1116 public ImageView primaryIcon;
1117 public ImageView secondaryActionButton;
1118 public View secondaryActionDivider;
Evan Millar5c22c3b2009-05-29 11:37:54 -07001119
Evan Millar15e514d2009-08-04 10:14:57 -07001120 // Need to keep track of this too
1121 ViewEntry entry;
1122 }
1123
1124 private final class ViewAdapter extends ContactEntryAdapter<ViewEntry>
1125 implements View.OnClickListener {
1126
Evan Millar5c22c3b2009-05-29 11:37:54 -07001127
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08001128 ViewAdapter(Context context, ArrayList<ArrayList<ViewEntry>> sections) {
1129 super(context, sections, SHOW_SEPARATORS);
1130 }
1131
Evan Millar15e514d2009-08-04 10:14:57 -07001132 public void onClick(View v) {
1133 Intent intent = (Intent) v.getTag();
1134 startActivity(intent);
1135 }
1136
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08001137 @Override
1138 public View getView(int position, View convertView, ViewGroup parent) {
Evan Millar5c22c3b2009-05-29 11:37:54 -07001139 ViewEntry entry = getEntry(mSections, position, false);
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08001140 View v;
1141
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08001142 ViewCache views;
1143
1144 // Check to see if we can reuse convertView
1145 if (convertView != null) {
1146 v = convertView;
1147 views = (ViewCache) v.getTag();
1148 } else {
1149 // Create a new view if needed
1150 v = mInflater.inflate(R.layout.list_item_text_icons, parent, false);
1151
1152 // Cache the children
1153 views = new ViewCache();
1154 views.label = (TextView) v.findViewById(android.R.id.text1);
1155 views.data = (TextView) v.findViewById(android.R.id.text2);
Jeff Sharkey624ddc32009-10-01 21:32:19 -07001156 views.footer = (TextView) v.findViewById(R.id.footer);
Evan Millar15e514d2009-08-04 10:14:57 -07001157 views.actionIcon = (ImageView) v.findViewById(R.id.action_icon);
1158 views.primaryIcon = (ImageView) v.findViewById(R.id.primary_icon);
1159 views.presenceIcon = (ImageView) v.findViewById(R.id.presence_icon);
1160 views.secondaryActionButton = (ImageView) v.findViewById(
1161 R.id.secondary_action_button);
1162 views.secondaryActionButton.setOnClickListener(this);
Evan Millar15e514d2009-08-04 10:14:57 -07001163 views.secondaryActionDivider = v.findViewById(R.id.divider);
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08001164 v.setTag(views);
1165 }
1166
1167 // Update the entry in the view cache
1168 views.entry = entry;
1169
1170 // Bind the data to the view
1171 bindView(v, entry);
1172 return v;
1173 }
1174
1175 @Override
1176 protected View newView(int position, ViewGroup parent) {
1177 // getView() handles this
1178 throw new UnsupportedOperationException();
1179 }
1180
1181 @Override
1182 protected void bindView(View view, ViewEntry entry) {
1183 final Resources resources = mContext.getResources();
1184 ViewCache views = (ViewCache) view.getTag();
1185
1186 // Set the label
1187 TextView label = views.label;
1188 setMaxLines(label, entry.maxLabelLines);
1189 label.setText(entry.label);
1190
1191 // Set the data
1192 TextView data = views.data;
1193 if (data != null) {
Evan Millar54a5c9f2009-06-23 17:41:09 -07001194 if (entry.mimetype.equals(Phone.CONTENT_ITEM_TYPE)
Jeff Sharkey49d17b32009-09-07 02:14:21 -07001195 || entry.mimetype.equals(Constants.MIME_SMS_ADDRESS)) {
Evan Millar54a5c9f2009-06-23 17:41:09 -07001196 data.setText(PhoneNumberUtils.formatNumber(entry.data));
1197 } else {
1198 data.setText(entry.data);
1199 }
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08001200 setMaxLines(data, entry.maxLines);
1201 }
1202
Jeff Sharkey624ddc32009-10-01 21:32:19 -07001203 // Set the footer
1204 if (!TextUtils.isEmpty(entry.footerLine)) {
1205 views.footer.setText(entry.footerLine);
1206 views.footer.setVisibility(View.VISIBLE);
1207 } else {
1208 views.footer.setVisibility(View.GONE);
1209 }
1210
Evan Millar15e514d2009-08-04 10:14:57 -07001211 // Set the primary icon
1212 views.primaryIcon.setVisibility(entry.isPrimary ? View.VISIBLE : View.GONE);
1213
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08001214 // Set the action icon
1215 ImageView action = views.actionIcon;
1216 if (entry.actionIcon != -1) {
Jeff Sharkeyab066932009-09-21 09:55:30 -07001217 Drawable actionIcon;
1218 if (entry.resPackageName != null) {
1219 // Load external resources through PackageManager
1220 actionIcon = mContext.getPackageManager().getDrawable(entry.resPackageName,
1221 entry.actionIcon, null);
1222 } else {
1223 actionIcon = resources.getDrawable(entry.actionIcon);
1224 }
1225 action.setImageDrawable(actionIcon);
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08001226 action.setVisibility(View.VISIBLE);
1227 } else {
1228 // Things should still line up as if there was an icon, so make it invisible
1229 action.setVisibility(View.INVISIBLE);
1230 }
1231
1232 // Set the presence icon
Bai Tao107736c2010-03-12 08:00:42 +08001233 Drawable presenceIcon = ContactPresenceIconUtil.getPresenceIcon(
1234 mContext, entry.presence);
Evan Millar15e514d2009-08-04 10:14:57 -07001235 ImageView presenceIconView = views.presenceIcon;
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08001236 if (presenceIcon != null) {
Evan Millar15e514d2009-08-04 10:14:57 -07001237 presenceIconView.setImageDrawable(presenceIcon);
1238 presenceIconView.setVisibility(View.VISIBLE);
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08001239 } else {
Evan Millar15e514d2009-08-04 10:14:57 -07001240 presenceIconView.setVisibility(View.GONE);
1241 }
1242
1243 // Set the secondary action button
1244 ImageView secondaryActionView = views.secondaryActionButton;
1245 Drawable secondaryActionIcon = null;
1246 if (entry.secondaryActionIcon != -1) {
1247 secondaryActionIcon = resources.getDrawable(entry.secondaryActionIcon);
1248 }
1249 if (entry.secondaryIntent != null && secondaryActionIcon != null) {
1250 secondaryActionView.setImageDrawable(secondaryActionIcon);
1251 secondaryActionView.setTag(entry.secondaryIntent);
1252 secondaryActionView.setVisibility(View.VISIBLE);
1253 views.secondaryActionDivider.setVisibility(View.VISIBLE);
1254 } else {
1255 secondaryActionView.setVisibility(View.GONE);
1256 views.secondaryActionDivider.setVisibility(View.GONE);
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08001257 }
1258 }
1259
1260 private void setMaxLines(TextView textView, int maxLines) {
1261 if (maxLines == 1) {
1262 textView.setSingleLine(true);
1263 textView.setEllipsize(TextUtils.TruncateAt.END);
1264 } else {
1265 textView.setSingleLine(false);
1266 textView.setMaxLines(maxLines);
1267 textView.setEllipsize(null);
1268 }
1269 }
1270 }
Jeff Sharkey624ddc32009-10-01 21:32:19 -07001271
1272 private interface StatusQuery {
1273 final String[] PROJECTION = new String[] {
1274 Data._ID,
1275 Data.STATUS,
1276 Data.STATUS_RES_PACKAGE,
1277 Data.STATUS_ICON,
1278 Data.STATUS_LABEL,
1279 Data.STATUS_TIMESTAMP,
1280 Data.PRESENCE,
1281 };
1282
1283 final int _ID = 0;
1284 }
Dmitri Plotnikov8e86b752010-02-22 17:47:57 -08001285
1286 @Override
1287 public void startSearch(String initialQuery, boolean selectInitialQuery, Bundle appSearchData,
1288 boolean globalSearch) {
1289 if (globalSearch) {
1290 super.startSearch(initialQuery, selectInitialQuery, appSearchData, globalSearch);
1291 } else {
1292 ContactsSearchManager.startSearch(this, initialQuery);
1293 }
1294 }
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08001295}