blob: 5313c287af77f6595a8ba06c26339b9eadc69040 [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;
Dmitri Plotnikov39466592009-07-27 11:23:51 -070061import android.provider.ContactsContract.RawContacts;
Fred Quintana8579e252009-12-07 14:50:12 -080062import android.provider.ContactsContract.RawContactsEntity;
Dmitri Plotnikov160e9da2009-09-30 23:48:12 -070063import android.provider.ContactsContract.StatusUpdates;
Jeff Sharkey624ddc32009-10-01 21:32:19 -070064import android.provider.ContactsContract.CommonDataKinds.Email;
65import android.provider.ContactsContract.CommonDataKinds.Im;
66import android.provider.ContactsContract.CommonDataKinds.Nickname;
67import android.provider.ContactsContract.CommonDataKinds.Note;
68import android.provider.ContactsContract.CommonDataKinds.Organization;
Evan Millar54a5c9f2009-06-23 17:41:09 -070069import android.provider.ContactsContract.CommonDataKinds.Phone;
Jeff Sharkey624ddc32009-10-01 21:32:19 -070070import android.provider.ContactsContract.CommonDataKinds.StructuredPostal;
Evan Millar951fa9c2009-10-26 14:57:43 -070071import android.provider.ContactsContract.CommonDataKinds.Website;
Evan Millar54a5c9f2009-06-23 17:41:09 -070072import android.telephony.PhoneNumberUtils;
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -080073import android.text.TextUtils;
74import android.util.Log;
75import android.view.ContextMenu;
76import android.view.KeyEvent;
Evan Millar11d628c2009-09-02 08:55:01 -070077import android.view.LayoutInflater;
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -080078import android.view.Menu;
Jeff Sharkey49d17b32009-09-07 02:14:21 -070079import android.view.MenuInflater;
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -080080import android.view.MenuItem;
81import android.view.View;
82import android.view.ViewGroup;
Evan Millar11d628c2009-09-02 08:55:01 -070083import android.view.Window;
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -080084import android.view.ContextMenu.ContextMenuInfo;
85import android.widget.AdapterView;
Evan Millar7911ff52009-07-21 15:55:18 -070086import android.widget.FrameLayout;
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -080087import android.widget.ImageView;
88import android.widget.ListView;
Evan Millarcdeea942009-10-07 11:47:35 -070089import android.widget.ScrollView;
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -080090import 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
158 private ArrayList<Entity> mEntities = Lists.newArrayList();
159 private HashMap<Long, DataStatus> mStatuses = Maps.newHashMap();
Evan Millar11d628c2009-09-02 08:55:01 -0700160
Makoto Onuki7a707382009-11-19 13:35:55 +0900161 /**
162 * The view shown if the detail list is empty.
163 * We set this to the list view when first bind the adapter, so that it won't be shown while
164 * we're loading data.
165 */
166 private View mEmptyView;
167
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800168 private ContentObserver mObserver = new ContentObserver(new Handler()) {
169 @Override
170 public boolean deliverSelfNotifications() {
171 return true;
172 }
173
174 @Override
175 public void onChange(boolean selfChange) {
Evan Millar11d628c2009-09-02 08:55:01 -0700176 if (mCursor != null && !mCursor.isClosed()) {
177 startEntityQuery();
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800178 }
179 }
180 };
181
182 public void onClick(DialogInterface dialog, int which) {
Dmitri Plotnikov99eafe72009-09-03 14:09:45 -0700183 closeCursor();
Megha Joshic508bd82009-10-01 17:31:49 -0700184 getContentResolver().delete(mLookupUri, null, null);
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800185 finish();
186 }
187
Evan Millar7911ff52009-07-21 15:55:18 -0700188 private ListView mListView;
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800189 private boolean mShowSmsLinksForAllPhones;
190
191 @Override
192 protected void onCreate(Bundle icicle) {
193 super.onCreate(icicle);
194
Evan Millar11d628c2009-09-02 08:55:01 -0700195 final Intent intent = getIntent();
Dmitri Plotnikov4e3a0432009-09-15 13:54:40 -0700196 Uri data = intent.getData();
197 String authority = data.getAuthority();
198 if (ContactsContract.AUTHORITY.equals(authority)) {
199 mLookupUri = data;
200 } else if (android.provider.Contacts.AUTHORITY.equals(authority)) {
201 final long rawContactId = ContentUris.parseId(data);
202 mLookupUri = RawContacts.getContactLookupUri(getContentResolver(),
203 ContentUris.withAppendedId(RawContacts.CONTENT_URI, rawContactId));
Dmitri Plotnikov83129f02009-09-02 19:04:41 -0700204
Dmitri Plotnikov4e3a0432009-09-15 13:54:40 -0700205 }
Dmitri Plotnikov83129f02009-09-02 19:04:41 -0700206 mInflater = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE);
Evan Millar11d628c2009-09-02 08:55:01 -0700207
208 requestWindowFeature(Window.FEATURE_NO_TITLE);
209 setContentView(R.layout.contact_card_layout);
210
211 mContactHeaderWidget = (ContactHeaderWidget) findViewById(R.id.contact_header_widget);
212 mContactHeaderWidget.showStar(true);
Evan Millar11d628c2009-09-02 08:55:01 -0700213 mContactHeaderWidget.setExcludeMimes(new String[] {
214 Contacts.CONTENT_ITEM_TYPE
215 });
216
Evan Millar11d628c2009-09-02 08:55:01 -0700217 mHandler = new NotifyingAsyncQueryHandler(this, this);
218
Evan Millarcdeea942009-10-07 11:47:35 -0700219 mListView = (ListView) findViewById(R.id.contact_data);
Evan Millar7911ff52009-07-21 15:55:18 -0700220 mListView.setOnCreateContextMenuListener(this);
221 mListView.setScrollBarStyle(ListView.SCROLLBARS_OUTSIDE_OVERLAY);
222 mListView.setOnItemClickListener(this);
Makoto Onuki7a707382009-11-19 13:35:55 +0900223 // Don't set it to mListView yet. We do so later when we bind the adapter.
224 mEmptyView = findViewById(android.R.id.empty);
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800225
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800226 mResolver = getContentResolver();
227
228 // Build the list of sections. The order they're added to mSections dictates the
229 // order they are displayed in the list.
230 mSections.add(mPhoneEntries);
231 mSections.add(mSmsEntries);
232 mSections.add(mEmailEntries);
233 mSections.add(mImEntries);
234 mSections.add(mPostalEntries);
Daniel Lehmanne12f8a92010-02-24 18:37:56 -0800235 mSections.add(mNicknameEntries);
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800236 mSections.add(mOrganizationEntries);
The Android Open Source Projectcac191e2009-03-18 22:20:27 -0700237 mSections.add(mGroupEntries);
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800238 mSections.add(mOtherEntries);
239
240 //TODO Read this value from a preference
241 mShowSmsLinksForAllPhones = true;
Evan Millar11d628c2009-09-02 08:55:01 -0700242 }
243
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800244 @Override
245 protected void onResume() {
246 super.onResume();
Evan Millar11d628c2009-09-02 08:55:01 -0700247 startEntityQuery();
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800248 }
249
250 @Override
251 protected void onPause() {
252 super.onPause();
Dmitri Plotnikov99eafe72009-09-03 14:09:45 -0700253 closeCursor();
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800254 }
255
256 @Override
257 protected void onDestroy() {
258 super.onDestroy();
Dmitri Plotnikov99eafe72009-09-03 14:09:45 -0700259 closeCursor();
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800260 }
261
262 @Override
263 protected Dialog onCreateDialog(int id) {
264 switch (id) {
265 case DIALOG_CONFIRM_DELETE:
266 return new AlertDialog.Builder(this)
267 .setTitle(R.string.deleteConfirmation_title)
268 .setIcon(android.R.drawable.ic_dialog_alert)
269 .setMessage(R.string.deleteConfirmation)
270 .setNegativeButton(android.R.string.cancel, null)
271 .setPositiveButton(android.R.string.ok, this)
272 .setCancelable(false)
273 .create();
Megha Joshid5afd8a2009-09-22 09:38:41 -0700274 case DIALOG_CONFIRM_READONLY_DELETE:
275 return new AlertDialog.Builder(this)
276 .setTitle(R.string.deleteConfirmation_title)
277 .setIcon(android.R.drawable.ic_dialog_alert)
278 .setMessage(R.string.readOnlyContactDeleteConfirmation)
279 .setNegativeButton(android.R.string.cancel, null)
280 .setPositiveButton(android.R.string.ok, this)
281 .setCancelable(false)
282 .create();
283 case DIALOG_CONFIRM_MULTIPLE_DELETE:
284 return new AlertDialog.Builder(this)
285 .setTitle(R.string.deleteConfirmation_title)
286 .setIcon(android.R.drawable.ic_dialog_alert)
287 .setMessage(R.string.multipleContactDeleteConfirmation)
288 .setNegativeButton(android.R.string.cancel, null)
289 .setPositiveButton(android.R.string.ok, this)
290 .setCancelable(false)
291 .create();
Evan Millarcdeea942009-10-07 11:47:35 -0700292 case DIALOG_CONFIRM_READONLY_HIDE: {
Megha Joshic508bd82009-10-01 17:31:49 -0700293 return new AlertDialog.Builder(this)
294 .setTitle(R.string.deleteConfirmation_title)
295 .setIcon(android.R.drawable.ic_dialog_alert)
296 .setMessage(R.string.readOnlyContactWarning)
297 .setPositiveButton(android.R.string.ok, this)
Evan Millarcdeea942009-10-07 11:47:35 -0700298 .create();
Megha Joshic508bd82009-10-01 17:31:49 -0700299 }
300
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800301 }
302 return null;
303 }
304
Evan Millar11d628c2009-09-02 08:55:01 -0700305 /** {@inheritDoc} */
Fred Quintana8579e252009-12-07 14:50:12 -0800306 public void onQueryComplete(int token, Object cookie, Cursor cursor) {
307 if (token == TOKEN_STATUSES) {
308 try {
309 // Read available social rows and consider binding
310 readStatuses(cursor);
311 } finally {
312 if (cursor != null) {
313 cursor.close();
314 }
315 }
316 } else {
317 EntityIterator iterator = RawContacts.newEntityIterator(cursor);
318 try {
319 // Read incoming entities and consider binding
320 readEntities(iterator);
321 } finally {
Evan Millar11d628c2009-09-02 08:55:01 -0700322 iterator.close();
323 }
324 }
Fred Quintana8579e252009-12-07 14:50:12 -0800325 considerBindData();
Evan Millar11d628c2009-09-02 08:55:01 -0700326 }
327
Evan Millar6a61a1a2009-09-29 14:00:43 -0700328 private long getRefreshedContactId() {
329 Uri freshContactUri = Contacts.lookupContact(getContentResolver(), mLookupUri);
330 if (freshContactUri != null) {
331 return ContentUris.parseId(freshContactUri);
332 }
333 return -1;
334 }
335
Jeff Sharkey624ddc32009-10-01 21:32:19 -0700336 /**
337 * Read from the given {@link EntityIterator} to build internal set of
338 * {@link #mEntities} for data display.
339 */
340 private synchronized void readEntities(EntityIterator iterator) {
341 mEntities.clear();
Evan Millar11d628c2009-09-02 08:55:01 -0700342 try {
343 while (iterator.hasNext()) {
Jeff Sharkey624ddc32009-10-01 21:32:19 -0700344 mEntities.add(iterator.next());
Evan Millar11d628c2009-09-02 08:55:01 -0700345 }
Jeff Sharkey624ddc32009-10-01 21:32:19 -0700346 mHasEntities = true;
Evan Millar11d628c2009-09-02 08:55:01 -0700347 } catch (RemoteException e) {
Jeff Sharkey624ddc32009-10-01 21:32:19 -0700348 Log.w(TAG, "Problem reading contact data: " + e.toString());
Evan Millar11d628c2009-09-02 08:55:01 -0700349 }
Evan Millar11d628c2009-09-02 08:55:01 -0700350 }
351
Jeff Sharkey624ddc32009-10-01 21:32:19 -0700352 /**
353 * Read from the given {@link Cursor} and build a set of {@link DataStatus}
354 * objects to match any valid statuses found.
355 */
356 private synchronized void readStatuses(Cursor cursor) {
357 mStatuses.clear();
358
359 // Walk found statuses, creating internal row for each
360 while (cursor.moveToNext()) {
361 final DataStatus status = new DataStatus(cursor);
362 final long dataId = cursor.getLong(StatusQuery._ID);
363 mStatuses.put(dataId, status);
364 }
365
366 mHasStatuses = true;
367 }
368
369 private synchronized void startEntityQuery() {
Dmitri Plotnikov99eafe72009-09-03 14:09:45 -0700370 closeCursor();
371
Evan Millar6a61a1a2009-09-29 14:00:43 -0700372 Uri uri = null;
Dmitri Plotnikov99eafe72009-09-03 14:09:45 -0700373 if (mLookupUri != null) {
374 mLookupUri = Contacts.getLookupUri(getContentResolver(), mLookupUri);
375 if (mLookupUri != null) {
Evan Millar6a61a1a2009-09-29 14:00:43 -0700376 uri = Contacts.lookupContact(getContentResolver(), mLookupUri);
Dmitri Plotnikov99eafe72009-09-03 14:09:45 -0700377 }
378 }
379
Evan Millar6a61a1a2009-09-29 14:00:43 -0700380 if (uri == null) {
Dmitri Plotnikov99eafe72009-09-03 14:09:45 -0700381
382 // TODO either figure out a way to prevent a flash of black background or
383 // use some other UI than a toast
384 Toast.makeText(this, R.string.invalidContactMessage, Toast.LENGTH_SHORT).show();
385 Log.e(TAG, "invalid contact uri: " + mLookupUri);
386 finish();
387 return;
388 }
389
Jeff Sharkey624ddc32009-10-01 21:32:19 -0700390 final Uri dataUri = Uri.withAppendedPath(uri, Contacts.Data.CONTENT_DIRECTORY);
391
392 // Keep stub cursor open on side to watch for change events
393 mCursor = mResolver.query(dataUri,
Dmitri Plotnikov99eafe72009-09-03 14:09:45 -0700394 new String[] {Contacts.DISPLAY_NAME}, null, null, null);
395 mCursor.registerContentObserver(mObserver);
396
Jeff Sharkey624ddc32009-10-01 21:32:19 -0700397 final long contactId = ContentUris.parseId(uri);
398
399 // Clear flags and start queries to data and status
400 mHasEntities = false;
401 mHasStatuses = false;
402
Fred Quintana8579e252009-12-07 14:50:12 -0800403 mHandler.startQuery(TOKEN_ENTITIES, null, RawContactsEntity.CONTENT_URI, null,
Jeff Sharkey624ddc32009-10-01 21:32:19 -0700404 RawContacts.CONTACT_ID + "=" + contactId, null, null);
405 mHandler.startQuery(TOKEN_STATUSES, null, dataUri, StatusQuery.PROJECTION,
406 StatusUpdates.PRESENCE + " IS NOT NULL OR " + StatusUpdates.STATUS
407 + " IS NOT NULL", null, null);
Dmitri Plotnikov99eafe72009-09-03 14:09:45 -0700408
Evan Millar9b1a1242009-09-16 10:26:10 -0700409 mContactHeaderWidget.bindFromContactLookupUri(mLookupUri);
Dmitri Plotnikov99eafe72009-09-03 14:09:45 -0700410 }
411
412 private void closeCursor() {
413 if (mCursor != null) {
414 mCursor.unregisterContentObserver(mObserver);
415 mCursor.close();
416 mCursor = null;
417 }
Evan Millar11d628c2009-09-02 08:55:01 -0700418 }
419
Jeff Sharkey624ddc32009-10-01 21:32:19 -0700420 /**
421 * Consider binding views after any of several background queries has
422 * completed. We check internal flags and only bind when all data has
423 * arrived.
424 */
425 private void considerBindData() {
426 if (mHasEntities && mHasStatuses) {
427 bindData();
428 }
429 }
430
Evan Millar11d628c2009-09-02 08:55:01 -0700431 private void bindData() {
432
433 // Build up the contact entries
434 buildEntries();
435
436 // Collapse similar data items in select sections.
437 Collapser.collapseList(mPhoneEntries);
438 Collapser.collapseList(mSmsEntries);
439 Collapser.collapseList(mEmailEntries);
440 Collapser.collapseList(mPostalEntries);
Jeff Sharkey624ddc32009-10-01 21:32:19 -0700441 Collapser.collapseList(mImEntries);
Evan Millar11d628c2009-09-02 08:55:01 -0700442
443 if (mAdapter == null) {
444 mAdapter = new ViewAdapter(this, mSections);
445 mListView.setAdapter(mAdapter);
446 } else {
447 mAdapter.setSections(mSections, SHOW_SEPARATORS);
448 }
Makoto Onuki7a707382009-11-19 13:35:55 +0900449 mListView.setEmptyView(mEmptyView);
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800450 }
451
452 @Override
453 public boolean onCreateOptionsMenu(Menu menu) {
Jeff Sharkey49d17b32009-09-07 02:14:21 -0700454 super.onCreateOptionsMenu(menu);
455
456 final MenuInflater inflater = getMenuInflater();
457 inflater.inflate(R.menu.view, menu);
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800458 return true;
459 }
460
461 @Override
462 public boolean onPrepareOptionsMenu(Menu menu) {
463 super.onPrepareOptionsMenu(menu);
Dmitri Plotnikovb4491ee2009-06-15 09:31:02 -0700464
Evan Millar51514db2009-09-22 11:42:35 -0700465 // Only allow edit when we have at least one raw_contact id
466 final boolean hasRawContact = (mRawContactIds.size() > 0);
Evan Millar2e1b8af2009-09-24 09:39:21 -0700467 menu.findItem(R.id.menu_edit).setEnabled(hasRawContact);
Jeff Sharkey49d17b32009-09-07 02:14:21 -0700468
Jeff Sharkeye0408762009-11-03 12:57:16 -0800469 // Only allow share when unrestricted contacts available
470 menu.findItem(R.id.menu_share).setEnabled(!mAllRestricted);
471
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800472 return true;
473 }
474
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800475 @Override
476 public void onCreateContextMenu(ContextMenu menu, View view, ContextMenuInfo menuInfo) {
477 AdapterView.AdapterContextMenuInfo info;
478 try {
479 info = (AdapterView.AdapterContextMenuInfo) menuInfo;
480 } catch (ClassCastException e) {
481 Log.e(TAG, "bad menuInfo", e);
482 return;
483 }
484
485 // This can be null sometimes, don't crash...
486 if (info == null) {
487 Log.e(TAG, "bad menuInfo");
488 return;
489 }
Evan Millar5c22c3b2009-05-29 11:37:54 -0700490
Evan Millar45e0ed32009-06-01 16:44:38 -0700491 ViewEntry entry = ContactEntryAdapter.getEntry(mSections, info.position, SHOW_SEPARATORS);
Neel Parekha8fb68a2009-09-25 18:05:18 -0700492 menu.setHeaderTitle(R.string.contactOptionsTitle);
Evan Millar45e0ed32009-06-01 16:44:38 -0700493 if (entry.mimetype.equals(CommonDataKinds.Phone.CONTENT_ITEM_TYPE)) {
494 menu.add(0, 0, 0, R.string.menu_call).setIntent(entry.intent);
Evan Millar15e514d2009-08-04 10:14:57 -0700495 menu.add(0, 0, 0, R.string.menu_sendSMS).setIntent(entry.secondaryIntent);
496 if (!entry.isPrimary) {
Evan Millar45e0ed32009-06-01 16:44:38 -0700497 menu.add(0, MENU_ITEM_MAKE_DEFAULT, 0, R.string.menu_makeDefaultNumber);
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800498 }
Evan Millar45e0ed32009-06-01 16:44:38 -0700499 } else if (entry.mimetype.equals(CommonDataKinds.Email.CONTENT_ITEM_TYPE)) {
500 menu.add(0, 0, 0, R.string.menu_sendEmail).setIntent(entry.intent);
Evan Millar15e514d2009-08-04 10:14:57 -0700501 if (!entry.isPrimary) {
Evan Millar45e0ed32009-06-01 16:44:38 -0700502 menu.add(0, MENU_ITEM_MAKE_DEFAULT, 0, R.string.menu_makeDefaultEmail);
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800503 }
Jeff Sharkeyc6ad3ab2009-07-21 19:30:15 -0700504 } else if (entry.mimetype.equals(CommonDataKinds.StructuredPostal.CONTENT_ITEM_TYPE)) {
Evan Millar45e0ed32009-06-01 16:44:38 -0700505 menu.add(0, 0, 0, R.string.menu_viewAddress).setIntent(entry.intent);
506 }
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800507 }
508
509 @Override
510 public boolean onOptionsItemSelected(MenuItem item) {
511 switch (item.getItemId()) {
Jeff Sharkey49d17b32009-09-07 02:14:21 -0700512 case R.id.menu_edit: {
Evan Millardf181202009-09-30 15:15:51 -0700513 Long rawContactIdToEdit = null;
514 if (mRawContactIds.size() > 0) {
515 rawContactIdToEdit = mRawContactIds.get(0);
516 } else {
517 // There is no rawContact to edit.
518 break;
Evan Millardb5d88c2009-08-28 09:31:57 -0700519 }
Evan Millar2e1b8af2009-09-24 09:39:21 -0700520 Uri rawContactUri = ContentUris.withAppendedId(RawContacts.CONTENT_URI,
521 rawContactIdToEdit);
Evan Millar9cd81242009-09-26 16:02:31 -0700522 startActivityForResult(new Intent(Intent.ACTION_EDIT, rawContactUri),
523 REQUEST_EDIT_CONTACT);
Evan Millar8a79cee2009-08-19 17:20:49 -0700524 break;
525 }
Jeff Sharkey49d17b32009-09-07 02:14:21 -0700526 case R.id.menu_delete: {
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800527 // Get confirmation
Evan Millarcdeea942009-10-07 11:47:35 -0700528 if (mReadOnlySourcesCnt > 0 & mWritableSourcesCnt > 0) {
Megha Joshid5afd8a2009-09-22 09:38:41 -0700529 showDialog(DIALOG_CONFIRM_READONLY_DELETE);
Evan Millarcdeea942009-10-07 11:47:35 -0700530 } else if (mReadOnlySourcesCnt > 0 && mWritableSourcesCnt == 0) {
Megha Joshic508bd82009-10-01 17:31:49 -0700531 showDialog(DIALOG_CONFIRM_READONLY_HIDE);
Evan Millarcdeea942009-10-07 11:47:35 -0700532 } else if (mReadOnlySourcesCnt == 0 && mWritableSourcesCnt > 1) {
533 showDialog(DIALOG_CONFIRM_MULTIPLE_DELETE);
534 } else {
535 showDialog(DIALOG_CONFIRM_DELETE);
536 }
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800537 return true;
538 }
Jeff Sharkey49d17b32009-09-07 02:14:21 -0700539 case R.id.menu_join: {
Dmitri Plotnikov49f705f2009-06-17 18:31:56 -0700540 showJoinAggregateActivity();
541 return true;
542 }
Jeff Sharkey49d17b32009-09-07 02:14:21 -0700543 case R.id.menu_options: {
Dmitri Plotnikovef038722009-06-24 18:51:47 -0700544 showOptionsActivity();
545 return true;
546 }
Jeff Sharkey49d17b32009-09-07 02:14:21 -0700547 case R.id.menu_share: {
Jeff Sharkeye0408762009-11-03 12:57:16 -0800548 if (mAllRestricted) return false;
549
Jeff Sharkey93049312009-09-27 17:28:13 -0700550 // TODO: Keep around actual LOOKUP_KEY, or formalize method of extracting
551 final String lookupKey = mLookupUri.getPathSegments().get(2);
552 final Uri shareUri = Uri.withAppendedPath(Contacts.CONTENT_VCARD_URI, lookupKey);
553
Jeff Sharkey49d17b32009-09-07 02:14:21 -0700554 final Intent intent = new Intent(Intent.ACTION_SEND);
Jeff Sharkey93049312009-09-27 17:28:13 -0700555 intent.setType(Contacts.CONTENT_VCARD_TYPE);
556 intent.putExtra(Intent.EXTRA_STREAM, shareUri);
Dmitri Plotnikovef038722009-06-24 18:51:47 -0700557
Jeff Sharkey49d17b32009-09-07 02:14:21 -0700558 // Launch chooser to share contact via
559 final CharSequence chooseTitle = getText(R.string.share_via);
560 final Intent chooseIntent = Intent.createChooser(intent, chooseTitle);
Evan Millar5c22c3b2009-05-29 11:37:54 -0700561
Jeff Sharkey49d17b32009-09-07 02:14:21 -0700562 try {
563 startActivity(chooseIntent);
564 } catch (ActivityNotFoundException ex) {
565 Toast.makeText(this, R.string.share_error, Toast.LENGTH_SHORT).show();
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800566 }
Jeff Sharkey49d17b32009-09-07 02:14:21 -0700567 return true;
568 }
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800569 }
570 return super.onOptionsItemSelected(item);
571 }
Evan Millar5c22c3b2009-05-29 11:37:54 -0700572
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800573 @Override
574 public boolean onContextItemSelected(MenuItem item) {
575 switch (item.getItemId()) {
576 case MENU_ITEM_MAKE_DEFAULT: {
Dmitri Plotnikovb4491ee2009-06-15 09:31:02 -0700577 if (makeItemDefault(item)) {
578 return true;
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800579 }
Dmitri Plotnikovb4491ee2009-06-15 09:31:02 -0700580 break;
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800581 }
582 }
Dmitri Plotnikovb4491ee2009-06-15 09:31:02 -0700583
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800584 return super.onContextItemSelected(item);
585 }
586
Dmitri Plotnikovb4491ee2009-06-15 09:31:02 -0700587 private boolean makeItemDefault(MenuItem item) {
588 ViewEntry entry = getViewEntryForMenuItem(item);
589 if (entry == null) {
590 return false;
591 }
592
593 // Update the primary values in the data record.
Evan Millar14fecb62009-09-09 09:23:12 -0700594 ContentValues values = new ContentValues(1);
Evan Millar54a5c9f2009-06-23 17:41:09 -0700595 values.put(Data.IS_SUPER_PRIMARY, 1);
Dmitri Plotnikovb4491ee2009-06-15 09:31:02 -0700596 getContentResolver().update(ContentUris.withAppendedId(Data.CONTENT_URI, entry.id),
597 values, null, null);
Evan Millar11d628c2009-09-02 08:55:01 -0700598 startEntityQuery();
Dmitri Plotnikovb4491ee2009-06-15 09:31:02 -0700599 return true;
600 }
601
602 /**
Dmitri Plotnikov49f705f2009-06-17 18:31:56 -0700603 * Shows a list of aggregates that can be joined into the currently viewed aggregate.
Dmitri Plotnikovb4491ee2009-06-15 09:31:02 -0700604 */
Dmitri Plotnikov49f705f2009-06-17 18:31:56 -0700605 public void showJoinAggregateActivity() {
Evan Millar6a61a1a2009-09-29 14:00:43 -0700606 long freshId = getRefreshedContactId();
607 if (freshId > 0) {
608 String displayName = null;
609 if (mCursor.moveToFirst()) {
610 displayName = mCursor.getString(0);
611 }
612 Intent intent = new Intent(ContactsListActivity.JOIN_AGGREGATE);
613 intent.putExtra(ContactsListActivity.EXTRA_AGGREGATE_ID, freshId);
614 if (displayName != null) {
615 intent.putExtra(ContactsListActivity.EXTRA_AGGREGATE_NAME, displayName);
616 }
617 startActivityForResult(intent, REQUEST_JOIN_CONTACT);
Dmitri Plotnikova5cfca32009-09-29 18:13:08 -0700618 }
Dmitri Plotnikov49f705f2009-06-17 18:31:56 -0700619 }
Dmitri Plotnikovb4491ee2009-06-15 09:31:02 -0700620
Dmitri Plotnikov49f705f2009-06-17 18:31:56 -0700621 @Override
622 protected void onActivityResult(int requestCode, int resultCode, Intent intent) {
Evan Millar2e1b8af2009-09-24 09:39:21 -0700623 if (requestCode == REQUEST_JOIN_CONTACT) {
624 if (resultCode == RESULT_OK && intent != null) {
625 final long contactId = ContentUris.parseId(intent.getData());
626 joinAggregate(contactId);
Evan Millar8a79cee2009-08-19 17:20:49 -0700627 }
Evan Millar9cd81242009-09-26 16:02:31 -0700628 } else if (requestCode == REQUEST_EDIT_CONTACT) {
Evan Millardf181202009-09-30 15:15:51 -0700629 if (resultCode == EditContactActivity.RESULT_CLOSE_VIEW_ACTIVITY) {
630 finish();
Jeff Hamilton5297c6a2009-10-01 02:22:33 -0700631 } else if (resultCode == Activity.RESULT_OK) {
Dmitri Plotnikov160e9da2009-09-30 23:48:12 -0700632 mLookupUri = intent.getData();
633 if (mLookupUri == null) {
634 finish();
635 }
Evan Millardf181202009-09-30 15:15:51 -0700636 }
Dmitri Plotnikov49f705f2009-06-17 18:31:56 -0700637 }
638 }
639
Dmitri Plotnikov99eafe72009-09-03 14:09:45 -0700640 private void splitContact(long rawContactId) {
Dmitri Plotnikovf40ccdf2009-09-04 17:37:15 -0700641 setAggregationException(rawContactId, AggregationExceptions.TYPE_KEEP_SEPARATE);
Dmitri Plotnikov99eafe72009-09-03 14:09:45 -0700642
643 // The split operation may have removed the original aggregate contact, so we need
644 // to requery everything
Dmitri Plotnikov040dc152009-09-03 15:17:56 -0700645 Toast.makeText(this, R.string.contactsSplitMessage, Toast.LENGTH_LONG).show();
Dmitri Plotnikov99eafe72009-09-03 14:09:45 -0700646 startEntityQuery();
Dmitri Plotnikov49f705f2009-06-17 18:31:56 -0700647 }
648
Dmitri Plotnikovf40ccdf2009-09-04 17:37:15 -0700649 private void joinAggregate(final long contactId) {
Dmitri Plotnikov39466592009-07-27 11:23:51 -0700650 Cursor c = mResolver.query(RawContacts.CONTENT_URI, new String[] {RawContacts._ID},
Dmitri Plotnikovf40ccdf2009-09-04 17:37:15 -0700651 RawContacts.CONTACT_ID + "=" + contactId, null, null);
Dmitri Plotnikov49f705f2009-06-17 18:31:56 -0700652
653 try {
654 while(c.moveToNext()) {
Dmitri Plotnikovf40ccdf2009-09-04 17:37:15 -0700655 long rawContactId = c.getLong(0);
656 setAggregationException(rawContactId, AggregationExceptions.TYPE_KEEP_TOGETHER);
Dmitri Plotnikov49f705f2009-06-17 18:31:56 -0700657 }
658 } finally {
659 c.close();
660 }
661
Dmitri Plotnikov040dc152009-09-03 15:17:56 -0700662 Toast.makeText(this, R.string.contactsJoinedMessage, Toast.LENGTH_LONG).show();
663 startEntityQuery();
Dmitri Plotnikov49f705f2009-06-17 18:31:56 -0700664 }
665
666 /**
667 * Given a contact ID sets an aggregation exception to either join the contact with the
668 * current aggregate or split off.
669 */
Dmitri Plotnikov99eafe72009-09-03 14:09:45 -0700670 protected void setAggregationException(long rawContactId, int exceptionType) {
Dmitri Plotnikovd09f75c2009-06-16 11:59:22 -0700671 ContentValues values = new ContentValues(3);
Dmitri Plotnikovf40ccdf2009-09-04 17:37:15 -0700672 for (long aRawContactId : mRawContactIds) {
673 if (aRawContactId != rawContactId) {
674 values.put(AggregationExceptions.RAW_CONTACT_ID1, aRawContactId);
675 values.put(AggregationExceptions.RAW_CONTACT_ID2, rawContactId);
676 values.put(AggregationExceptions.TYPE, exceptionType);
677 mResolver.update(AggregationExceptions.CONTENT_URI, values, null, null);
678 }
679 }
Dmitri Plotnikovb4491ee2009-06-15 09:31:02 -0700680 }
681
Dmitri Plotnikovef038722009-06-24 18:51:47 -0700682 private void showOptionsActivity() {
683 final Intent intent = new Intent(this, ContactOptionsActivity.class);
Evan Millar6a61a1a2009-09-29 14:00:43 -0700684 intent.setData(mLookupUri);
Dmitri Plotnikovef038722009-06-24 18:51:47 -0700685 startActivity(intent);
686 }
687
Dmitri Plotnikovb4491ee2009-06-15 09:31:02 -0700688 private ViewEntry getViewEntryForMenuItem(MenuItem item) {
689 AdapterView.AdapterContextMenuInfo info;
690 try {
691 info = (AdapterView.AdapterContextMenuInfo) item.getMenuInfo();
692 } catch (ClassCastException e) {
693 Log.e(TAG, "bad menuInfo", e);
694 return null;
695 }
696
697 return ContactEntryAdapter.getEntry(mSections, info.position, SHOW_SEPARATORS);
698 }
699
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800700 @Override
701 public boolean onKeyDown(int keyCode, KeyEvent event) {
702 switch (keyCode) {
703 case KeyEvent.KEYCODE_CALL: {
704 try {
705 ITelephony phone = ITelephony.Stub.asInterface(
706 ServiceManager.checkService("phone"));
707 if (phone != null && !phone.isIdle()) {
708 // Skip out and let the key be handled at a higher level
709 break;
710 }
711 } catch (RemoteException re) {
712 // Fall through and try to call the contact
713 }
714
Evan Millar7911ff52009-07-21 15:55:18 -0700715 int index = mListView.getSelectedItemPosition();
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800716 if (index != -1) {
717 ViewEntry entry = ViewAdapter.getEntry(mSections, index, SHOW_SEPARATORS);
Evan Millar66388be2009-05-28 15:41:07 -0700718 if (entry.intent.getAction() == Intent.ACTION_CALL_PRIVILEGED) {
719 startActivity(entry.intent);
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800720 }
Jeff Sharkey827762d2009-12-23 11:06:08 -0800721 } else if (mPrimaryPhoneUri != null) {
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800722 // There isn't anything selected, call the default number
Jeff Sharkey827762d2009-12-23 11:06:08 -0800723 final Intent intent = new Intent(Intent.ACTION_CALL_PRIVILEGED,
724 mPrimaryPhoneUri);
725 startActivity(intent);
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800726 }
727 return true;
728 }
729
730 case KeyEvent.KEYCODE_DEL: {
Evan Millarcdeea942009-10-07 11:47:35 -0700731 if (mReadOnlySourcesCnt > 0 & mWritableSourcesCnt > 0) {
Megha Joshid5afd8a2009-09-22 09:38:41 -0700732 showDialog(DIALOG_CONFIRM_READONLY_DELETE);
Evan Millarcdeea942009-10-07 11:47:35 -0700733 } else if (mReadOnlySourcesCnt > 0 && mWritableSourcesCnt == 0) {
Megha Joshic508bd82009-10-01 17:31:49 -0700734 showDialog(DIALOG_CONFIRM_READONLY_HIDE);
Evan Millarcdeea942009-10-07 11:47:35 -0700735 } else if (mReadOnlySourcesCnt == 0 && mWritableSourcesCnt > 1) {
736 showDialog(DIALOG_CONFIRM_MULTIPLE_DELETE);
737 } else {
738 showDialog(DIALOG_CONFIRM_DELETE);
739 }
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800740 return true;
741 }
742 }
743
744 return super.onKeyDown(keyCode, event);
745 }
746
Evan Millar7911ff52009-07-21 15:55:18 -0700747 public void onItemClick(AdapterView parent, View v, int position, long id) {
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800748 ViewEntry entry = ViewAdapter.getEntry(mSections, position, SHOW_SEPARATORS);
749 if (entry != null) {
750 Intent intent = entry.intent;
Evan Millardf181202009-09-30 15:15:51 -0700751 if (intent != null) {
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800752 try {
753 startActivity(intent);
754 } catch (ActivityNotFoundException e) {
755 Log.e(TAG, "No activity found for intent: " + intent);
756 signalError();
757 }
758 } else {
759 signalError();
760 }
761 } else {
762 signalError();
763 }
764 }
765
766 /**
767 * Signal an error to the user via a beep, or some other method.
768 */
769 private void signalError() {
770 //TODO: implement this when we have the sonification APIs
771 }
772
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800773 /**
774 * Build up the entries to display on the screen.
Evan Millar5c22c3b2009-05-29 11:37:54 -0700775 *
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800776 * @param personCursor the URI for the contact being displayed
777 */
Evan Millar11d628c2009-09-02 08:55:01 -0700778 private final void buildEntries() {
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800779 // Clear out the old entries
780 final int numSections = mSections.size();
781 for (int i = 0; i < numSections; i++) {
782 mSections.get(i).clear();
783 }
784
Evan Millar7911ff52009-07-21 15:55:18 -0700785 mRawContactIds.clear();
Jeff Sharkeye0408762009-11-03 12:57:16 -0800786
Megha Joshid5afd8a2009-09-22 09:38:41 -0700787 mReadOnlySourcesCnt = 0;
Jeff Sharkey624ddc32009-10-01 21:32:19 -0700788 mWritableSourcesCnt = 0;
Jeff Sharkeye0408762009-11-03 12:57:16 -0800789 mAllRestricted = true;
Jeff Sharkey827762d2009-12-23 11:06:08 -0800790 mPrimaryPhoneUri = null;
Jeff Sharkeye0408762009-11-03 12:57:16 -0800791
Megha Joshid5afd8a2009-09-22 09:38:41 -0700792 mWritableRawContactIds.clear();
Dmitri Plotnikovb4491ee2009-06-15 09:31:02 -0700793
Jeff Sharkey624ddc32009-10-01 21:32:19 -0700794 final Context context = this;
795 final Sources sources = Sources.getInstance(context);
Evan Millar11d628c2009-09-02 08:55:01 -0700796
Evan Millar66388be2009-05-28 15:41:07 -0700797 // Build up method entries
Evan Millar6a61a1a2009-09-29 14:00:43 -0700798 if (mLookupUri != null) {
Evan Millar11d628c2009-09-02 08:55:01 -0700799 for (Entity entity: mEntities) {
800 final ContentValues entValues = entity.getEntityValues();
801 final String accountType = entValues.getAsString(RawContacts.ACCOUNT_TYPE);
Jeff Sharkeyab066932009-09-21 09:55:30 -0700802 final long rawContactId = entValues.getAsLong(RawContacts._ID);
Evan Millar7911ff52009-07-21 15:55:18 -0700803
Jeff Sharkeye0408762009-11-03 12:57:16 -0800804 // Mark when this contact has any unrestricted components
805 final boolean isRestricted = entValues.getAsInteger(RawContacts.IS_RESTRICTED) != 0;
806 if (!isRestricted) mAllRestricted = false;
807
Dmitri Plotnikov2a8cef02009-09-23 19:01:15 -0700808 if (!mRawContactIds.contains(rawContactId)) {
809 mRawContactIds.add(rawContactId);
810 }
Megha Joshic508bd82009-10-01 17:31:49 -0700811 ContactsSource contactsSource = sources.getInflatedSource(accountType,
812 ContactsSource.LEVEL_SUMMARY);
813 if (contactsSource != null && contactsSource.readOnly) {
814 mReadOnlySourcesCnt += 1;
815 } else {
816 mWritableSourcesCnt += 1;
Evan Millarcdeea942009-10-07 11:47:35 -0700817 mWritableRawContactIds.add(rawContactId);
818 }
Megha Joshic508bd82009-10-01 17:31:49 -0700819
Dmitri Plotnikov2a8cef02009-09-23 19:01:15 -0700820
Evan Millar11d628c2009-09-02 08:55:01 -0700821 for (NamedContentValues subValue : entity.getSubValues()) {
Jeff Sharkeyab066932009-09-21 09:55:30 -0700822 final ContentValues entryValues = subValue.values;
823 entryValues.put(Data.RAW_CONTACT_ID, rawContactId);
824
Jeff Sharkey624ddc32009-10-01 21:32:19 -0700825 final long dataId = entryValues.getAsLong(Data._ID);
826 final String mimeType = entryValues.getAsString(Data.MIMETYPE);
827 if (mimeType == null) continue;
Evan Millar66388be2009-05-28 15:41:07 -0700828
Jeff Sharkey624ddc32009-10-01 21:32:19 -0700829 final DataKind kind = sources.getKindOrFallback(accountType, mimeType, this,
Evan Millar11d628c2009-09-02 08:55:01 -0700830 ContactsSource.LEVEL_MIMETYPES);
Jeff Sharkeyab066932009-09-21 09:55:30 -0700831 if (kind == null) continue;
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800832
Jeff Sharkey624ddc32009-10-01 21:32:19 -0700833 final ViewEntry entry = ViewEntry.fromValues(context, mimeType, kind,
834 rawContactId, dataId, entryValues);
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800835
Jeff Sharkey624ddc32009-10-01 21:32:19 -0700836 final boolean hasData = !TextUtils.isEmpty(entry.data);
Evan Millarff04a272009-09-26 15:23:37 -0700837 final boolean isSuperPrimary = entryValues.getAsInteger(
838 Data.IS_SUPER_PRIMARY) != 0;
Evan Millar11d628c2009-09-02 08:55:01 -0700839
Jeff Sharkey624ddc32009-10-01 21:32:19 -0700840 if (Phone.CONTENT_ITEM_TYPE.equals(mimeType) && hasData) {
Evan Millarff04a272009-09-26 15:23:37 -0700841 // Build phone entries
842 mNumPhoneNumbers++;
Evan Millar11d628c2009-09-02 08:55:01 -0700843
Evan Millarff04a272009-09-26 15:23:37 -0700844 entry.intent = new Intent(Intent.ACTION_CALL_PRIVILEGED,
Jeff Sharkey624ddc32009-10-01 21:32:19 -0700845 Uri.fromParts(Constants.SCHEME_TEL, entry.data, null));
Evan Millarff04a272009-09-26 15:23:37 -0700846 entry.secondaryIntent = new Intent(Intent.ACTION_SENDTO,
Jeff Sharkey624ddc32009-10-01 21:32:19 -0700847 Uri.fromParts(Constants.SCHEME_SMSTO, entry.data, null));
Evan Millar49714ee2009-09-02 16:42:47 -0700848
Jeff Sharkey827762d2009-12-23 11:06:08 -0800849 // Remember super-primary phone
850 if (isSuperPrimary) mPrimaryPhoneUri = entry.uri;
851
Evan Millarff04a272009-09-26 15:23:37 -0700852 entry.isPrimary = isSuperPrimary;
853 mPhoneEntries.add(entry);
854
855 if (entry.type == CommonDataKinds.Phone.TYPE_MOBILE
856 || mShowSmsLinksForAllPhones) {
857 // Add an SMS entry
858 if (kind.iconAltRes > 0) {
859 entry.secondaryActionIcon = kind.iconAltRes;
Evan Millar49714ee2009-09-02 16:42:47 -0700860 }
Evan Millarff04a272009-09-26 15:23:37 -0700861 }
Jeff Sharkey624ddc32009-10-01 21:32:19 -0700862 } else if (Email.CONTENT_ITEM_TYPE.equals(mimeType) && hasData) {
Evan Millarff04a272009-09-26 15:23:37 -0700863 // Build email entries
864 entry.intent = new Intent(Intent.ACTION_SENDTO,
Jeff Sharkey624ddc32009-10-01 21:32:19 -0700865 Uri.fromParts(Constants.SCHEME_MAILTO, entry.data, null));
Evan Millarff04a272009-09-26 15:23:37 -0700866 entry.isPrimary = isSuperPrimary;
867 mEmailEntries.add(entry);
Jeff Sharkey624ddc32009-10-01 21:32:19 -0700868
869 // When Email rows have status, create additional Im row
870 final DataStatus status = mStatuses.get(entry.id);
871 if (status != null) {
872 final String imMime = Im.CONTENT_ITEM_TYPE;
873 final DataKind imKind = sources.getKindOrFallback(accountType,
874 imMime, this, ContactsSource.LEVEL_MIMETYPES);
875 final ViewEntry imEntry = ViewEntry.fromValues(context,
876 imMime, imKind, rawContactId, dataId, entryValues);
877 imEntry.intent = ContactsUtils.buildImIntent(entryValues);
878 imEntry.applyStatus(status, false);
879 mImEntries.add(imEntry);
880 }
881 } else if (StructuredPostal.CONTENT_ITEM_TYPE.equals(mimeType) && hasData) {
Evan Millarff04a272009-09-26 15:23:37 -0700882 // Build postal entries
883 entry.maxLines = 4;
Jeff Sharkey624ddc32009-10-01 21:32:19 -0700884 entry.intent = new Intent(Intent.ACTION_VIEW, entry.uri);
Evan Millarff04a272009-09-26 15:23:37 -0700885 mPostalEntries.add(entry);
Jeff Sharkey624ddc32009-10-01 21:32:19 -0700886 } else if (Im.CONTENT_ITEM_TYPE.equals(mimeType) && hasData) {
887 // Build IM entries
888 entry.intent = ContactsUtils.buildImIntent(entryValues);
Evan Millarff04a272009-09-26 15:23:37 -0700889 if (TextUtils.isEmpty(entry.label)) {
890 entry.label = getString(R.string.chat).toLowerCase();
891 }
Evan Millar11d628c2009-09-02 08:55:01 -0700892
Jeff Sharkey624ddc32009-10-01 21:32:19 -0700893 // Apply presence and status details when available
894 final DataStatus status = mStatuses.get(entry.id);
895 if (status != null) {
896 entry.applyStatus(status, false);
Evan Millarff04a272009-09-26 15:23:37 -0700897 }
Evan Millarff04a272009-09-26 15:23:37 -0700898 mImEntries.add(entry);
Daniel Lehmanne12f8a92010-02-24 18:37:56 -0800899 } else if (Organization.CONTENT_ITEM_TYPE.equals(mimeType) &&
900 (hasData || !TextUtils.isEmpty(entry.label))) {
901 // Build organization entries
Evan Millarff04a272009-09-26 15:23:37 -0700902 entry.uri = null;
Evan Millar11d628c2009-09-02 08:55:01 -0700903 mOrganizationEntries.add(entry);
Daniel Lehmanne12f8a92010-02-24 18:37:56 -0800904 } else if (Nickname.CONTENT_ITEM_TYPE.equals(mimeType) && hasData) {
905 // Build nickname entries
906 entry.uri = null;
907 mNicknameEntries.add(entry);
Jeff Sharkey624ddc32009-10-01 21:32:19 -0700908 } else if (Note.CONTENT_ITEM_TYPE.equals(mimeType) && hasData) {
Evan Millar11d628c2009-09-02 08:55:01 -0700909 // Build note entries
Evan Millar11d628c2009-09-02 08:55:01 -0700910 entry.uri = null;
Evan Millar11d628c2009-09-02 08:55:01 -0700911 entry.maxLines = 10;
912 mOtherEntries.add(entry);
Evan Millar951fa9c2009-10-26 14:57:43 -0700913 } else if (Website.CONTENT_ITEM_TYPE.equals(mimeType) && hasData) {
914 // Build note entries
915 entry.uri = null;
916 entry.maxLines = 10;
917 try {
918 WebAddress webAddress = new WebAddress(entry.data);
Makoto Onuki7a707382009-11-19 13:35:55 +0900919 entry.intent = new Intent(Intent.ACTION_VIEW,
Evan Millar951fa9c2009-10-26 14:57:43 -0700920 Uri.parse(webAddress.toString()));
921 } catch (ParseException e) {
922 Log.e(TAG, "Couldn't parse website: " + entry.data);
923 }
924 mOtherEntries.add(entry);
Jeff Sharkeyab066932009-09-21 09:55:30 -0700925 } else {
Jeff Sharkey624ddc32009-10-01 21:32:19 -0700926 // Handle showing custom rows
Evan Millarff04a272009-09-26 15:23:37 -0700927 entry.intent = new Intent(Intent.ACTION_VIEW, entry.uri);
Jeff Sharkey624ddc32009-10-01 21:32:19 -0700928
929 // Use social summary when requested by external source
930 final DataStatus status = mStatuses.get(entry.id);
931 final boolean hasSocial = kind.actionBodySocial && status != null;
932 if (hasSocial) {
933 entry.applyStatus(status, true);
934 }
935
936 if (hasSocial || hasData) {
937 mOtherEntries.add(entry);
938 }
Evan Millar11d628c2009-09-02 08:55:01 -0700939 }
Evan Millar2e1b8af2009-09-24 09:39:21 -0700940 }
Evan Millar66388be2009-05-28 15:41:07 -0700941 }
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800942 }
943 }
944
Jeff Sharkey624ddc32009-10-01 21:32:19 -0700945 static String buildActionString(DataKind kind, ContentValues values, boolean lowerCase,
946 Context context) {
Evan Millar11d628c2009-09-02 08:55:01 -0700947 if (kind.actionHeader == null) {
948 return null;
Jeff Hamilton8350e5b2009-03-24 21:01:34 -0700949 }
Jeff Sharkey624ddc32009-10-01 21:32:19 -0700950 CharSequence actionHeader = kind.actionHeader.inflateUsing(context, values);
Evan Millar11d628c2009-09-02 08:55:01 -0700951 if (actionHeader == null) {
952 return null;
953 }
954 return lowerCase ? actionHeader.toString().toLowerCase() : actionHeader.toString();
955 }
Jeff Hamilton8350e5b2009-03-24 21:01:34 -0700956
Jeff Sharkey624ddc32009-10-01 21:32:19 -0700957 static String buildDataString(DataKind kind, ContentValues values, Context context) {
Evan Millar11d628c2009-09-02 08:55:01 -0700958 if (kind.actionBody == null) {
959 return null;
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800960 }
Jeff Sharkey624ddc32009-10-01 21:32:19 -0700961 CharSequence actionBody = kind.actionBody.inflateUsing(context, values);
Evan Millar11d628c2009-09-02 08:55:01 -0700962 return actionBody == null ? null : actionBody.toString();
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800963 }
Evan Millar5c22c3b2009-05-29 11:37:54 -0700964
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800965 /**
966 * A basic structure with the data for a contact entry in the list.
967 */
Jeff Sharkey624ddc32009-10-01 21:32:19 -0700968 static class ViewEntry extends ContactEntryAdapter.Entry implements Collapsible<ViewEntry> {
969 public Context context = null;
Jeff Sharkeyab066932009-09-21 09:55:30 -0700970 public String resPackageName = null;
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800971 public int actionIcon = -1;
Evan Millar15e514d2009-08-04 10:14:57 -0700972 public boolean isPrimary = false;
Evan Millar15e514d2009-08-04 10:14:57 -0700973 public int secondaryActionIcon = -1;
974 public Intent intent;
975 public Intent secondaryIntent = null;
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800976 public int maxLabelLines = 1;
Evan Millar54a5c9f2009-06-23 17:41:09 -0700977 public ArrayList<Long> ids = new ArrayList<Long>();
978 public int collapseCount = 0;
979
Jeff Sharkey624ddc32009-10-01 21:32:19 -0700980 public int presence = -1;
981 public int presenceIcon = -1;
982
983 public CharSequence footerLine = null;
984
985 private ViewEntry() {
986 }
987
988 /**
989 * Build new {@link ViewEntry} and populate from the given values.
990 */
991 public static ViewEntry fromValues(Context context, String mimeType, DataKind kind,
992 long rawContactId, long dataId, ContentValues values) {
993 final ViewEntry entry = new ViewEntry();
994 entry.context = context;
995 entry.contactId = rawContactId;
996 entry.id = dataId;
997 entry.uri = ContentUris.withAppendedId(Data.CONTENT_URI, entry.id);
998 entry.mimetype = mimeType;
999 entry.label = buildActionString(kind, values, false, context);
1000 entry.data = buildDataString(kind, values, context);
1001
1002 if (kind.typeColumn != null && values.containsKey(kind.typeColumn)) {
1003 entry.type = values.getAsInteger(kind.typeColumn);
1004 }
1005 if (kind.iconRes > 0) {
1006 entry.resPackageName = kind.resPackageName;
1007 entry.actionIcon = kind.iconRes;
1008 }
1009
1010 return entry;
1011 }
1012
1013 /**
1014 * Apply given {@link DataStatus} values over this {@link ViewEntry}
1015 *
1016 * @param fillData When true, the given status replaces {@link #data}
1017 * and {@link #footerLine}. Otherwise only {@link #presence}
1018 * is updated.
1019 */
1020 public ViewEntry applyStatus(DataStatus status, boolean fillData) {
1021 presence = status.getPresence();
1022 presenceIcon = (presence == -1) ? -1 :
1023 StatusUpdates.getPresenceIconResourceId(this.presence);
1024
1025 if (fillData && status.isValid()) {
1026 this.data = status.getStatus().toString();
1027 this.footerLine = status.getTimestampLabel(context);
1028 }
1029
1030 return this;
1031 }
1032
Evan Millar54a5c9f2009-06-23 17:41:09 -07001033 public boolean collapseWith(ViewEntry entry) {
1034 // assert equal collapse keys
Evan Millaradb0f8c2009-09-28 17:20:50 -07001035 if (!shouldCollapseWith(entry)) {
Evan Millar54a5c9f2009-06-23 17:41:09 -07001036 return false;
1037 }
1038
1039 // Choose the label associated with the highest type precedence.
1040 if (TypePrecedence.getTypePrecedence(mimetype, type)
1041 > TypePrecedence.getTypePrecedence(entry.mimetype, entry.type)) {
1042 type = entry.type;
1043 label = entry.label;
1044 }
1045
1046 // Choose the max of the maxLines and maxLabelLines values.
1047 maxLines = Math.max(maxLines, entry.maxLines);
1048 maxLabelLines = Math.max(maxLabelLines, entry.maxLabelLines);
1049
1050 // Choose the presence with the highest precedence.
Jeff Sharkey624ddc32009-10-01 21:32:19 -07001051 if (StatusUpdates.getPresencePrecedence(presence)
1052 < StatusUpdates.getPresencePrecedence(entry.presence)) {
1053 presence = entry.presence;
Evan Millar54a5c9f2009-06-23 17:41:09 -07001054 }
1055
1056 // If any of the collapsed entries are primary make the whole thing primary.
Evan Millar15e514d2009-08-04 10:14:57 -07001057 isPrimary = entry.isPrimary ? true : isPrimary;
Evan Millar54a5c9f2009-06-23 17:41:09 -07001058
1059 // uri, and contactdId, shouldn't make a difference. Just keep the original.
1060
1061 // Keep track of all the ids that have been collapsed with this one.
1062 ids.add(entry.id);
1063 collapseCount++;
1064 return true;
1065 }
1066
Evan Millaradb0f8c2009-09-28 17:20:50 -07001067 public boolean shouldCollapseWith(ViewEntry entry) {
1068 if (entry == null) {
1069 return false;
1070 }
1071
Makoto Onukic710b0e2009-10-13 15:43:24 -07001072 if (!ContactsUtils.areDataEqual(context, mimetype, data, entry.mimetype, entry.data)) {
1073 return false;
Evan Millaradb0f8c2009-09-28 17:20:50 -07001074 }
1075
Makoto Onukic710b0e2009-10-13 15:43:24 -07001076 if (!TextUtils.equals(mimetype, entry.mimetype)
1077 || !ContactsUtils.areIntentActionEqual(intent, entry.intent)
1078 || !ContactsUtils.areIntentActionEqual(secondaryIntent, entry.secondaryIntent)
Evan Millaradb0f8c2009-09-28 17:20:50 -07001079 || actionIcon != entry.actionIcon) {
1080 return false;
1081 }
1082
1083 return true;
1084 }
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08001085 }
1086
Evan Millar15e514d2009-08-04 10:14:57 -07001087 /** Cache of the children views of a row */
1088 static class ViewCache {
1089 public TextView label;
1090 public TextView data;
Jeff Sharkey624ddc32009-10-01 21:32:19 -07001091 public TextView footer;
Evan Millar15e514d2009-08-04 10:14:57 -07001092 public ImageView actionIcon;
1093 public ImageView presenceIcon;
1094 public ImageView primaryIcon;
1095 public ImageView secondaryActionButton;
1096 public View secondaryActionDivider;
Evan Millar5c22c3b2009-05-29 11:37:54 -07001097
Evan Millar15e514d2009-08-04 10:14:57 -07001098 // Need to keep track of this too
1099 ViewEntry entry;
1100 }
1101
1102 private final class ViewAdapter extends ContactEntryAdapter<ViewEntry>
1103 implements View.OnClickListener {
1104
Evan Millar5c22c3b2009-05-29 11:37:54 -07001105
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08001106 ViewAdapter(Context context, ArrayList<ArrayList<ViewEntry>> sections) {
1107 super(context, sections, SHOW_SEPARATORS);
1108 }
1109
Evan Millar15e514d2009-08-04 10:14:57 -07001110 public void onClick(View v) {
1111 Intent intent = (Intent) v.getTag();
1112 startActivity(intent);
1113 }
1114
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08001115 @Override
1116 public View getView(int position, View convertView, ViewGroup parent) {
Evan Millar5c22c3b2009-05-29 11:37:54 -07001117 ViewEntry entry = getEntry(mSections, position, false);
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08001118 View v;
1119
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08001120 ViewCache views;
1121
1122 // Check to see if we can reuse convertView
1123 if (convertView != null) {
1124 v = convertView;
1125 views = (ViewCache) v.getTag();
1126 } else {
1127 // Create a new view if needed
1128 v = mInflater.inflate(R.layout.list_item_text_icons, parent, false);
1129
1130 // Cache the children
1131 views = new ViewCache();
1132 views.label = (TextView) v.findViewById(android.R.id.text1);
1133 views.data = (TextView) v.findViewById(android.R.id.text2);
Jeff Sharkey624ddc32009-10-01 21:32:19 -07001134 views.footer = (TextView) v.findViewById(R.id.footer);
Evan Millar15e514d2009-08-04 10:14:57 -07001135 views.actionIcon = (ImageView) v.findViewById(R.id.action_icon);
1136 views.primaryIcon = (ImageView) v.findViewById(R.id.primary_icon);
1137 views.presenceIcon = (ImageView) v.findViewById(R.id.presence_icon);
1138 views.secondaryActionButton = (ImageView) v.findViewById(
1139 R.id.secondary_action_button);
1140 views.secondaryActionButton.setOnClickListener(this);
Evan Millar15e514d2009-08-04 10:14:57 -07001141 views.secondaryActionDivider = v.findViewById(R.id.divider);
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08001142 v.setTag(views);
1143 }
1144
1145 // Update the entry in the view cache
1146 views.entry = entry;
1147
1148 // Bind the data to the view
1149 bindView(v, entry);
1150 return v;
1151 }
1152
1153 @Override
1154 protected View newView(int position, ViewGroup parent) {
1155 // getView() handles this
1156 throw new UnsupportedOperationException();
1157 }
1158
1159 @Override
1160 protected void bindView(View view, ViewEntry entry) {
1161 final Resources resources = mContext.getResources();
1162 ViewCache views = (ViewCache) view.getTag();
1163
1164 // Set the label
1165 TextView label = views.label;
1166 setMaxLines(label, entry.maxLabelLines);
1167 label.setText(entry.label);
1168
1169 // Set the data
1170 TextView data = views.data;
1171 if (data != null) {
Evan Millar54a5c9f2009-06-23 17:41:09 -07001172 if (entry.mimetype.equals(Phone.CONTENT_ITEM_TYPE)
Jeff Sharkey49d17b32009-09-07 02:14:21 -07001173 || entry.mimetype.equals(Constants.MIME_SMS_ADDRESS)) {
Evan Millar54a5c9f2009-06-23 17:41:09 -07001174 data.setText(PhoneNumberUtils.formatNumber(entry.data));
1175 } else {
1176 data.setText(entry.data);
1177 }
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08001178 setMaxLines(data, entry.maxLines);
1179 }
1180
Jeff Sharkey624ddc32009-10-01 21:32:19 -07001181 // Set the footer
1182 if (!TextUtils.isEmpty(entry.footerLine)) {
1183 views.footer.setText(entry.footerLine);
1184 views.footer.setVisibility(View.VISIBLE);
1185 } else {
1186 views.footer.setVisibility(View.GONE);
1187 }
1188
Evan Millar15e514d2009-08-04 10:14:57 -07001189 // Set the primary icon
1190 views.primaryIcon.setVisibility(entry.isPrimary ? View.VISIBLE : View.GONE);
1191
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08001192 // Set the action icon
1193 ImageView action = views.actionIcon;
1194 if (entry.actionIcon != -1) {
Jeff Sharkeyab066932009-09-21 09:55:30 -07001195 Drawable actionIcon;
1196 if (entry.resPackageName != null) {
1197 // Load external resources through PackageManager
1198 actionIcon = mContext.getPackageManager().getDrawable(entry.resPackageName,
1199 entry.actionIcon, null);
1200 } else {
1201 actionIcon = resources.getDrawable(entry.actionIcon);
1202 }
1203 action.setImageDrawable(actionIcon);
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08001204 action.setVisibility(View.VISIBLE);
1205 } else {
1206 // Things should still line up as if there was an icon, so make it invisible
1207 action.setVisibility(View.INVISIBLE);
1208 }
1209
1210 // Set the presence icon
1211 Drawable presenceIcon = null;
Evan Millar15e514d2009-08-04 10:14:57 -07001212 if (entry.presenceIcon != -1) {
1213 presenceIcon = resources.getDrawable(entry.presenceIcon);
Jeff Sharkey624ddc32009-10-01 21:32:19 -07001214 } else if (entry.presence != -1) {
Evan Millar54a5c9f2009-06-23 17:41:09 -07001215 presenceIcon = resources.getDrawable(
Jeff Sharkey624ddc32009-10-01 21:32:19 -07001216 StatusUpdates.getPresenceIconResourceId(entry.presence));
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08001217 }
Evan Millar15e514d2009-08-04 10:14:57 -07001218 ImageView presenceIconView = views.presenceIcon;
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08001219 if (presenceIcon != null) {
Evan Millar15e514d2009-08-04 10:14:57 -07001220 presenceIconView.setImageDrawable(presenceIcon);
1221 presenceIconView.setVisibility(View.VISIBLE);
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08001222 } else {
Evan Millar15e514d2009-08-04 10:14:57 -07001223 presenceIconView.setVisibility(View.GONE);
1224 }
1225
1226 // Set the secondary action button
1227 ImageView secondaryActionView = views.secondaryActionButton;
1228 Drawable secondaryActionIcon = null;
1229 if (entry.secondaryActionIcon != -1) {
1230 secondaryActionIcon = resources.getDrawable(entry.secondaryActionIcon);
1231 }
1232 if (entry.secondaryIntent != null && secondaryActionIcon != null) {
1233 secondaryActionView.setImageDrawable(secondaryActionIcon);
1234 secondaryActionView.setTag(entry.secondaryIntent);
1235 secondaryActionView.setVisibility(View.VISIBLE);
1236 views.secondaryActionDivider.setVisibility(View.VISIBLE);
1237 } else {
1238 secondaryActionView.setVisibility(View.GONE);
1239 views.secondaryActionDivider.setVisibility(View.GONE);
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08001240 }
1241 }
1242
1243 private void setMaxLines(TextView textView, int maxLines) {
1244 if (maxLines == 1) {
1245 textView.setSingleLine(true);
1246 textView.setEllipsize(TextUtils.TruncateAt.END);
1247 } else {
1248 textView.setSingleLine(false);
1249 textView.setMaxLines(maxLines);
1250 textView.setEllipsize(null);
1251 }
1252 }
1253 }
Jeff Sharkey624ddc32009-10-01 21:32:19 -07001254
1255 private interface StatusQuery {
1256 final String[] PROJECTION = new String[] {
1257 Data._ID,
1258 Data.STATUS,
1259 Data.STATUS_RES_PACKAGE,
1260 Data.STATUS_ICON,
1261 Data.STATUS_LABEL,
1262 Data.STATUS_TIMESTAMP,
1263 Data.PRESENCE,
1264 };
1265
1266 final int _ID = 0;
1267 }
Dmitri Plotnikov8e86b752010-02-22 17:47:57 -08001268
1269 @Override
1270 public void startSearch(String initialQuery, boolean selectInitialQuery, Bundle appSearchData,
1271 boolean globalSearch) {
1272 if (globalSearch) {
1273 super.startSearch(initialQuery, selectInitialQuery, appSearchData, globalSearch);
1274 } else {
1275 ContactsSearchManager.startSearch(this, initialQuery);
1276 }
1277 }
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08001278}