blob: 000f129883257e14a1025e8dbdeb43bd36db0e75 [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
Daniel Lehmanncbf75952010-03-12 13:29:07 -0800551 final String lookupKey = Uri.encode(mLookupUri.getPathSegments().get(2));
Jeff Sharkey93049312009-09-27 17:28:13 -0700552 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);
Bai Tao67205ed2010-03-16 08:09:11 +0800718 if (entry != null &&
719 entry.intent.getAction() == Intent.ACTION_CALL_PRIVILEGED) {
Evan Millar66388be2009-05-28 15:41:07 -0700720 startActivity(entry.intent);
Bai Tao67205ed2010-03-16 08:09:11 +0800721 return true;
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800722 }
Jeff Sharkey827762d2009-12-23 11:06:08 -0800723 } else if (mPrimaryPhoneUri != null) {
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800724 // There isn't anything selected, call the default number
Jeff Sharkey827762d2009-12-23 11:06:08 -0800725 final Intent intent = new Intent(Intent.ACTION_CALL_PRIVILEGED,
726 mPrimaryPhoneUri);
727 startActivity(intent);
Bai Tao67205ed2010-03-16 08:09:11 +0800728 return true;
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800729 }
Bai Tao67205ed2010-03-16 08:09:11 +0800730 return false;
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800731 }
732
733 case KeyEvent.KEYCODE_DEL: {
Evan Millarcdeea942009-10-07 11:47:35 -0700734 if (mReadOnlySourcesCnt > 0 & mWritableSourcesCnt > 0) {
Megha Joshid5afd8a2009-09-22 09:38:41 -0700735 showDialog(DIALOG_CONFIRM_READONLY_DELETE);
Evan Millarcdeea942009-10-07 11:47:35 -0700736 } else if (mReadOnlySourcesCnt > 0 && mWritableSourcesCnt == 0) {
Megha Joshic508bd82009-10-01 17:31:49 -0700737 showDialog(DIALOG_CONFIRM_READONLY_HIDE);
Evan Millarcdeea942009-10-07 11:47:35 -0700738 } else if (mReadOnlySourcesCnt == 0 && mWritableSourcesCnt > 1) {
739 showDialog(DIALOG_CONFIRM_MULTIPLE_DELETE);
740 } else {
741 showDialog(DIALOG_CONFIRM_DELETE);
742 }
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800743 return true;
744 }
745 }
746
747 return super.onKeyDown(keyCode, event);
748 }
749
Evan Millar7911ff52009-07-21 15:55:18 -0700750 public void onItemClick(AdapterView parent, View v, int position, long id) {
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800751 ViewEntry entry = ViewAdapter.getEntry(mSections, position, SHOW_SEPARATORS);
752 if (entry != null) {
753 Intent intent = entry.intent;
Evan Millardf181202009-09-30 15:15:51 -0700754 if (intent != null) {
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800755 try {
756 startActivity(intent);
757 } catch (ActivityNotFoundException e) {
758 Log.e(TAG, "No activity found for intent: " + intent);
759 signalError();
760 }
761 } else {
762 signalError();
763 }
764 } else {
765 signalError();
766 }
767 }
768
769 /**
770 * Signal an error to the user via a beep, or some other method.
771 */
772 private void signalError() {
773 //TODO: implement this when we have the sonification APIs
774 }
775
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800776 /**
777 * Build up the entries to display on the screen.
Evan Millar5c22c3b2009-05-29 11:37:54 -0700778 *
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800779 * @param personCursor the URI for the contact being displayed
780 */
Evan Millar11d628c2009-09-02 08:55:01 -0700781 private final void buildEntries() {
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800782 // Clear out the old entries
783 final int numSections = mSections.size();
784 for (int i = 0; i < numSections; i++) {
785 mSections.get(i).clear();
786 }
787
Evan Millar7911ff52009-07-21 15:55:18 -0700788 mRawContactIds.clear();
Jeff Sharkeye0408762009-11-03 12:57:16 -0800789
Megha Joshid5afd8a2009-09-22 09:38:41 -0700790 mReadOnlySourcesCnt = 0;
Jeff Sharkey624ddc32009-10-01 21:32:19 -0700791 mWritableSourcesCnt = 0;
Jeff Sharkeye0408762009-11-03 12:57:16 -0800792 mAllRestricted = true;
Jeff Sharkey827762d2009-12-23 11:06:08 -0800793 mPrimaryPhoneUri = null;
Jeff Sharkeye0408762009-11-03 12:57:16 -0800794
Megha Joshid5afd8a2009-09-22 09:38:41 -0700795 mWritableRawContactIds.clear();
Dmitri Plotnikovb4491ee2009-06-15 09:31:02 -0700796
Jeff Sharkey624ddc32009-10-01 21:32:19 -0700797 final Context context = this;
798 final Sources sources = Sources.getInstance(context);
Evan Millar11d628c2009-09-02 08:55:01 -0700799
Evan Millar66388be2009-05-28 15:41:07 -0700800 // Build up method entries
Evan Millar6a61a1a2009-09-29 14:00:43 -0700801 if (mLookupUri != null) {
Evan Millar11d628c2009-09-02 08:55:01 -0700802 for (Entity entity: mEntities) {
803 final ContentValues entValues = entity.getEntityValues();
804 final String accountType = entValues.getAsString(RawContacts.ACCOUNT_TYPE);
Jeff Sharkeyab066932009-09-21 09:55:30 -0700805 final long rawContactId = entValues.getAsLong(RawContacts._ID);
Evan Millar7911ff52009-07-21 15:55:18 -0700806
Jeff Sharkeye0408762009-11-03 12:57:16 -0800807 // Mark when this contact has any unrestricted components
808 final boolean isRestricted = entValues.getAsInteger(RawContacts.IS_RESTRICTED) != 0;
809 if (!isRestricted) mAllRestricted = false;
810
Dmitri Plotnikov2a8cef02009-09-23 19:01:15 -0700811 if (!mRawContactIds.contains(rawContactId)) {
812 mRawContactIds.add(rawContactId);
813 }
Megha Joshic508bd82009-10-01 17:31:49 -0700814 ContactsSource contactsSource = sources.getInflatedSource(accountType,
815 ContactsSource.LEVEL_SUMMARY);
816 if (contactsSource != null && contactsSource.readOnly) {
817 mReadOnlySourcesCnt += 1;
818 } else {
819 mWritableSourcesCnt += 1;
Evan Millarcdeea942009-10-07 11:47:35 -0700820 mWritableRawContactIds.add(rawContactId);
821 }
Megha Joshic508bd82009-10-01 17:31:49 -0700822
Dmitri Plotnikov2a8cef02009-09-23 19:01:15 -0700823
Evan Millar11d628c2009-09-02 08:55:01 -0700824 for (NamedContentValues subValue : entity.getSubValues()) {
Jeff Sharkeyab066932009-09-21 09:55:30 -0700825 final ContentValues entryValues = subValue.values;
826 entryValues.put(Data.RAW_CONTACT_ID, rawContactId);
827
Jeff Sharkey624ddc32009-10-01 21:32:19 -0700828 final long dataId = entryValues.getAsLong(Data._ID);
829 final String mimeType = entryValues.getAsString(Data.MIMETYPE);
830 if (mimeType == null) continue;
Evan Millar66388be2009-05-28 15:41:07 -0700831
Jeff Sharkey624ddc32009-10-01 21:32:19 -0700832 final DataKind kind = sources.getKindOrFallback(accountType, mimeType, this,
Evan Millar11d628c2009-09-02 08:55:01 -0700833 ContactsSource.LEVEL_MIMETYPES);
Jeff Sharkeyab066932009-09-21 09:55:30 -0700834 if (kind == null) continue;
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800835
Jeff Sharkey624ddc32009-10-01 21:32:19 -0700836 final ViewEntry entry = ViewEntry.fromValues(context, mimeType, kind,
837 rawContactId, dataId, entryValues);
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800838
Jeff Sharkey624ddc32009-10-01 21:32:19 -0700839 final boolean hasData = !TextUtils.isEmpty(entry.data);
Evan Millarff04a272009-09-26 15:23:37 -0700840 final boolean isSuperPrimary = entryValues.getAsInteger(
841 Data.IS_SUPER_PRIMARY) != 0;
Evan Millar11d628c2009-09-02 08:55:01 -0700842
Jeff Sharkey624ddc32009-10-01 21:32:19 -0700843 if (Phone.CONTENT_ITEM_TYPE.equals(mimeType) && hasData) {
Evan Millarff04a272009-09-26 15:23:37 -0700844 // Build phone entries
845 mNumPhoneNumbers++;
Evan Millar11d628c2009-09-02 08:55:01 -0700846
Evan Millarff04a272009-09-26 15:23:37 -0700847 entry.intent = new Intent(Intent.ACTION_CALL_PRIVILEGED,
Jeff Sharkey624ddc32009-10-01 21:32:19 -0700848 Uri.fromParts(Constants.SCHEME_TEL, entry.data, null));
Evan Millarff04a272009-09-26 15:23:37 -0700849 entry.secondaryIntent = new Intent(Intent.ACTION_SENDTO,
Jeff Sharkey624ddc32009-10-01 21:32:19 -0700850 Uri.fromParts(Constants.SCHEME_SMSTO, entry.data, null));
Evan Millar49714ee2009-09-02 16:42:47 -0700851
Jeff Sharkey827762d2009-12-23 11:06:08 -0800852 // Remember super-primary phone
853 if (isSuperPrimary) mPrimaryPhoneUri = entry.uri;
854
Evan Millarff04a272009-09-26 15:23:37 -0700855 entry.isPrimary = isSuperPrimary;
856 mPhoneEntries.add(entry);
857
858 if (entry.type == CommonDataKinds.Phone.TYPE_MOBILE
859 || mShowSmsLinksForAllPhones) {
860 // Add an SMS entry
861 if (kind.iconAltRes > 0) {
862 entry.secondaryActionIcon = kind.iconAltRes;
Evan Millar49714ee2009-09-02 16:42:47 -0700863 }
Evan Millarff04a272009-09-26 15:23:37 -0700864 }
Jeff Sharkey624ddc32009-10-01 21:32:19 -0700865 } else if (Email.CONTENT_ITEM_TYPE.equals(mimeType) && hasData) {
Evan Millarff04a272009-09-26 15:23:37 -0700866 // Build email entries
867 entry.intent = new Intent(Intent.ACTION_SENDTO,
Jeff Sharkey624ddc32009-10-01 21:32:19 -0700868 Uri.fromParts(Constants.SCHEME_MAILTO, entry.data, null));
Evan Millarff04a272009-09-26 15:23:37 -0700869 entry.isPrimary = isSuperPrimary;
870 mEmailEntries.add(entry);
Jeff Sharkey624ddc32009-10-01 21:32:19 -0700871
872 // When Email rows have status, create additional Im row
873 final DataStatus status = mStatuses.get(entry.id);
874 if (status != null) {
875 final String imMime = Im.CONTENT_ITEM_TYPE;
876 final DataKind imKind = sources.getKindOrFallback(accountType,
877 imMime, this, ContactsSource.LEVEL_MIMETYPES);
878 final ViewEntry imEntry = ViewEntry.fromValues(context,
879 imMime, imKind, rawContactId, dataId, entryValues);
880 imEntry.intent = ContactsUtils.buildImIntent(entryValues);
881 imEntry.applyStatus(status, false);
882 mImEntries.add(imEntry);
883 }
884 } else if (StructuredPostal.CONTENT_ITEM_TYPE.equals(mimeType) && hasData) {
Evan Millarff04a272009-09-26 15:23:37 -0700885 // Build postal entries
886 entry.maxLines = 4;
Jeff Sharkey624ddc32009-10-01 21:32:19 -0700887 entry.intent = new Intent(Intent.ACTION_VIEW, entry.uri);
Evan Millarff04a272009-09-26 15:23:37 -0700888 mPostalEntries.add(entry);
Jeff Sharkey624ddc32009-10-01 21:32:19 -0700889 } else if (Im.CONTENT_ITEM_TYPE.equals(mimeType) && hasData) {
890 // Build IM entries
891 entry.intent = ContactsUtils.buildImIntent(entryValues);
Evan Millarff04a272009-09-26 15:23:37 -0700892 if (TextUtils.isEmpty(entry.label)) {
893 entry.label = getString(R.string.chat).toLowerCase();
894 }
Evan Millar11d628c2009-09-02 08:55:01 -0700895
Jeff Sharkey624ddc32009-10-01 21:32:19 -0700896 // Apply presence and status details when available
897 final DataStatus status = mStatuses.get(entry.id);
898 if (status != null) {
899 entry.applyStatus(status, false);
Evan Millarff04a272009-09-26 15:23:37 -0700900 }
Evan Millarff04a272009-09-26 15:23:37 -0700901 mImEntries.add(entry);
Daniel Lehmanne12f8a92010-02-24 18:37:56 -0800902 } else if (Organization.CONTENT_ITEM_TYPE.equals(mimeType) &&
903 (hasData || !TextUtils.isEmpty(entry.label))) {
904 // Build organization entries
Evan Millarff04a272009-09-26 15:23:37 -0700905 entry.uri = null;
Evan Millar11d628c2009-09-02 08:55:01 -0700906 mOrganizationEntries.add(entry);
Daniel Lehmanne12f8a92010-02-24 18:37:56 -0800907 } else if (Nickname.CONTENT_ITEM_TYPE.equals(mimeType) && hasData) {
908 // Build nickname entries
909 entry.uri = null;
910 mNicknameEntries.add(entry);
Jeff Sharkey624ddc32009-10-01 21:32:19 -0700911 } else if (Note.CONTENT_ITEM_TYPE.equals(mimeType) && hasData) {
Evan Millar11d628c2009-09-02 08:55:01 -0700912 // Build note entries
Evan Millar11d628c2009-09-02 08:55:01 -0700913 entry.uri = null;
Daniel Lehmann0e322012010-02-25 16:09:46 -0800914 entry.maxLines = 100;
Evan Millar11d628c2009-09-02 08:55:01 -0700915 mOtherEntries.add(entry);
Evan Millar951fa9c2009-10-26 14:57:43 -0700916 } else if (Website.CONTENT_ITEM_TYPE.equals(mimeType) && hasData) {
917 // Build note entries
918 entry.uri = null;
919 entry.maxLines = 10;
920 try {
921 WebAddress webAddress = new WebAddress(entry.data);
Makoto Onuki7a707382009-11-19 13:35:55 +0900922 entry.intent = new Intent(Intent.ACTION_VIEW,
Evan Millar951fa9c2009-10-26 14:57:43 -0700923 Uri.parse(webAddress.toString()));
924 } catch (ParseException e) {
925 Log.e(TAG, "Couldn't parse website: " + entry.data);
926 }
927 mOtherEntries.add(entry);
Jeff Sharkeyab066932009-09-21 09:55:30 -0700928 } else {
Jeff Sharkey624ddc32009-10-01 21:32:19 -0700929 // Handle showing custom rows
Evan Millarff04a272009-09-26 15:23:37 -0700930 entry.intent = new Intent(Intent.ACTION_VIEW, entry.uri);
Jeff Sharkey624ddc32009-10-01 21:32:19 -0700931
932 // Use social summary when requested by external source
933 final DataStatus status = mStatuses.get(entry.id);
934 final boolean hasSocial = kind.actionBodySocial && status != null;
935 if (hasSocial) {
936 entry.applyStatus(status, true);
937 }
938
939 if (hasSocial || hasData) {
940 mOtherEntries.add(entry);
941 }
Evan Millar11d628c2009-09-02 08:55:01 -0700942 }
Evan Millar2e1b8af2009-09-24 09:39:21 -0700943 }
Evan Millar66388be2009-05-28 15:41:07 -0700944 }
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800945 }
946 }
947
Jeff Sharkey624ddc32009-10-01 21:32:19 -0700948 static String buildActionString(DataKind kind, ContentValues values, boolean lowerCase,
949 Context context) {
Evan Millar11d628c2009-09-02 08:55:01 -0700950 if (kind.actionHeader == null) {
951 return null;
Jeff Hamilton8350e5b2009-03-24 21:01:34 -0700952 }
Jeff Sharkey624ddc32009-10-01 21:32:19 -0700953 CharSequence actionHeader = kind.actionHeader.inflateUsing(context, values);
Evan Millar11d628c2009-09-02 08:55:01 -0700954 if (actionHeader == null) {
955 return null;
956 }
957 return lowerCase ? actionHeader.toString().toLowerCase() : actionHeader.toString();
958 }
Jeff Hamilton8350e5b2009-03-24 21:01:34 -0700959
Jeff Sharkey624ddc32009-10-01 21:32:19 -0700960 static String buildDataString(DataKind kind, ContentValues values, Context context) {
Evan Millar11d628c2009-09-02 08:55:01 -0700961 if (kind.actionBody == null) {
962 return null;
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800963 }
Jeff Sharkey624ddc32009-10-01 21:32:19 -0700964 CharSequence actionBody = kind.actionBody.inflateUsing(context, values);
Evan Millar11d628c2009-09-02 08:55:01 -0700965 return actionBody == null ? null : actionBody.toString();
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800966 }
Evan Millar5c22c3b2009-05-29 11:37:54 -0700967
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800968 /**
969 * A basic structure with the data for a contact entry in the list.
970 */
Jeff Sharkey624ddc32009-10-01 21:32:19 -0700971 static class ViewEntry extends ContactEntryAdapter.Entry implements Collapsible<ViewEntry> {
972 public Context context = null;
Jeff Sharkeyab066932009-09-21 09:55:30 -0700973 public String resPackageName = null;
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800974 public int actionIcon = -1;
Evan Millar15e514d2009-08-04 10:14:57 -0700975 public boolean isPrimary = false;
Evan Millar15e514d2009-08-04 10:14:57 -0700976 public int secondaryActionIcon = -1;
977 public Intent intent;
978 public Intent secondaryIntent = null;
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800979 public int maxLabelLines = 1;
Evan Millar54a5c9f2009-06-23 17:41:09 -0700980 public ArrayList<Long> ids = new ArrayList<Long>();
981 public int collapseCount = 0;
982
Jeff Sharkey624ddc32009-10-01 21:32:19 -0700983 public int presence = -1;
Jeff Sharkey624ddc32009-10-01 21:32:19 -0700984
985 public CharSequence footerLine = null;
986
987 private ViewEntry() {
988 }
989
990 /**
991 * Build new {@link ViewEntry} and populate from the given values.
992 */
993 public static ViewEntry fromValues(Context context, String mimeType, DataKind kind,
994 long rawContactId, long dataId, ContentValues values) {
995 final ViewEntry entry = new ViewEntry();
996 entry.context = context;
997 entry.contactId = rawContactId;
998 entry.id = dataId;
999 entry.uri = ContentUris.withAppendedId(Data.CONTENT_URI, entry.id);
1000 entry.mimetype = mimeType;
1001 entry.label = buildActionString(kind, values, false, context);
1002 entry.data = buildDataString(kind, values, context);
1003
1004 if (kind.typeColumn != null && values.containsKey(kind.typeColumn)) {
1005 entry.type = values.getAsInteger(kind.typeColumn);
1006 }
1007 if (kind.iconRes > 0) {
1008 entry.resPackageName = kind.resPackageName;
1009 entry.actionIcon = kind.iconRes;
1010 }
1011
1012 return entry;
1013 }
1014
1015 /**
1016 * Apply given {@link DataStatus} values over this {@link ViewEntry}
1017 *
1018 * @param fillData When true, the given status replaces {@link #data}
1019 * and {@link #footerLine}. Otherwise only {@link #presence}
1020 * is updated.
1021 */
1022 public ViewEntry applyStatus(DataStatus status, boolean fillData) {
1023 presence = status.getPresence();
Jeff Sharkey624ddc32009-10-01 21:32:19 -07001024 if (fillData && status.isValid()) {
1025 this.data = status.getStatus().toString();
1026 this.footerLine = status.getTimestampLabel(context);
1027 }
1028
1029 return this;
1030 }
1031
Evan Millar54a5c9f2009-06-23 17:41:09 -07001032 public boolean collapseWith(ViewEntry entry) {
1033 // assert equal collapse keys
Evan Millaradb0f8c2009-09-28 17:20:50 -07001034 if (!shouldCollapseWith(entry)) {
Evan Millar54a5c9f2009-06-23 17:41:09 -07001035 return false;
1036 }
1037
1038 // Choose the label associated with the highest type precedence.
1039 if (TypePrecedence.getTypePrecedence(mimetype, type)
1040 > TypePrecedence.getTypePrecedence(entry.mimetype, entry.type)) {
1041 type = entry.type;
1042 label = entry.label;
1043 }
1044
1045 // Choose the max of the maxLines and maxLabelLines values.
1046 maxLines = Math.max(maxLines, entry.maxLines);
1047 maxLabelLines = Math.max(maxLabelLines, entry.maxLabelLines);
1048
1049 // Choose the presence with the highest precedence.
Jeff Sharkey624ddc32009-10-01 21:32:19 -07001050 if (StatusUpdates.getPresencePrecedence(presence)
1051 < StatusUpdates.getPresencePrecedence(entry.presence)) {
1052 presence = entry.presence;
Evan Millar54a5c9f2009-06-23 17:41:09 -07001053 }
1054
1055 // If any of the collapsed entries are primary make the whole thing primary.
Evan Millar15e514d2009-08-04 10:14:57 -07001056 isPrimary = entry.isPrimary ? true : isPrimary;
Evan Millar54a5c9f2009-06-23 17:41:09 -07001057
1058 // uri, and contactdId, shouldn't make a difference. Just keep the original.
1059
1060 // Keep track of all the ids that have been collapsed with this one.
1061 ids.add(entry.id);
1062 collapseCount++;
1063 return true;
1064 }
1065
Evan Millaradb0f8c2009-09-28 17:20:50 -07001066 public boolean shouldCollapseWith(ViewEntry entry) {
1067 if (entry == null) {
1068 return false;
1069 }
1070
Makoto Onukic710b0e2009-10-13 15:43:24 -07001071 if (!ContactsUtils.areDataEqual(context, mimetype, data, entry.mimetype, entry.data)) {
1072 return false;
Evan Millaradb0f8c2009-09-28 17:20:50 -07001073 }
1074
Makoto Onukic710b0e2009-10-13 15:43:24 -07001075 if (!TextUtils.equals(mimetype, entry.mimetype)
1076 || !ContactsUtils.areIntentActionEqual(intent, entry.intent)
1077 || !ContactsUtils.areIntentActionEqual(secondaryIntent, entry.secondaryIntent)
Evan Millaradb0f8c2009-09-28 17:20:50 -07001078 || actionIcon != entry.actionIcon) {
1079 return false;
1080 }
1081
1082 return true;
1083 }
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08001084 }
1085
Evan Millar15e514d2009-08-04 10:14:57 -07001086 /** Cache of the children views of a row */
1087 static class ViewCache {
1088 public TextView label;
1089 public TextView data;
Jeff Sharkey624ddc32009-10-01 21:32:19 -07001090 public TextView footer;
Evan Millar15e514d2009-08-04 10:14:57 -07001091 public ImageView actionIcon;
1092 public ImageView presenceIcon;
1093 public ImageView primaryIcon;
1094 public ImageView secondaryActionButton;
1095 public View secondaryActionDivider;
Evan Millar5c22c3b2009-05-29 11:37:54 -07001096
Evan Millar15e514d2009-08-04 10:14:57 -07001097 // Need to keep track of this too
1098 ViewEntry entry;
1099 }
1100
1101 private final class ViewAdapter extends ContactEntryAdapter<ViewEntry>
1102 implements View.OnClickListener {
1103
Evan Millar5c22c3b2009-05-29 11:37:54 -07001104
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08001105 ViewAdapter(Context context, ArrayList<ArrayList<ViewEntry>> sections) {
1106 super(context, sections, SHOW_SEPARATORS);
1107 }
1108
Evan Millar15e514d2009-08-04 10:14:57 -07001109 public void onClick(View v) {
1110 Intent intent = (Intent) v.getTag();
1111 startActivity(intent);
1112 }
1113
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08001114 @Override
1115 public View getView(int position, View convertView, ViewGroup parent) {
Evan Millar5c22c3b2009-05-29 11:37:54 -07001116 ViewEntry entry = getEntry(mSections, position, false);
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08001117 View v;
1118
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08001119 ViewCache views;
1120
1121 // Check to see if we can reuse convertView
1122 if (convertView != null) {
1123 v = convertView;
1124 views = (ViewCache) v.getTag();
1125 } else {
1126 // Create a new view if needed
1127 v = mInflater.inflate(R.layout.list_item_text_icons, parent, false);
1128
1129 // Cache the children
1130 views = new ViewCache();
1131 views.label = (TextView) v.findViewById(android.R.id.text1);
1132 views.data = (TextView) v.findViewById(android.R.id.text2);
Jeff Sharkey624ddc32009-10-01 21:32:19 -07001133 views.footer = (TextView) v.findViewById(R.id.footer);
Evan Millar15e514d2009-08-04 10:14:57 -07001134 views.actionIcon = (ImageView) v.findViewById(R.id.action_icon);
1135 views.primaryIcon = (ImageView) v.findViewById(R.id.primary_icon);
1136 views.presenceIcon = (ImageView) v.findViewById(R.id.presence_icon);
1137 views.secondaryActionButton = (ImageView) v.findViewById(
1138 R.id.secondary_action_button);
1139 views.secondaryActionButton.setOnClickListener(this);
Evan Millar15e514d2009-08-04 10:14:57 -07001140 views.secondaryActionDivider = v.findViewById(R.id.divider);
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08001141 v.setTag(views);
1142 }
1143
1144 // Update the entry in the view cache
1145 views.entry = entry;
1146
1147 // Bind the data to the view
1148 bindView(v, entry);
1149 return v;
1150 }
1151
1152 @Override
1153 protected View newView(int position, ViewGroup parent) {
1154 // getView() handles this
1155 throw new UnsupportedOperationException();
1156 }
1157
1158 @Override
1159 protected void bindView(View view, ViewEntry entry) {
1160 final Resources resources = mContext.getResources();
1161 ViewCache views = (ViewCache) view.getTag();
1162
1163 // Set the label
1164 TextView label = views.label;
1165 setMaxLines(label, entry.maxLabelLines);
1166 label.setText(entry.label);
1167
1168 // Set the data
1169 TextView data = views.data;
1170 if (data != null) {
Evan Millar54a5c9f2009-06-23 17:41:09 -07001171 if (entry.mimetype.equals(Phone.CONTENT_ITEM_TYPE)
Jeff Sharkey49d17b32009-09-07 02:14:21 -07001172 || entry.mimetype.equals(Constants.MIME_SMS_ADDRESS)) {
Evan Millar54a5c9f2009-06-23 17:41:09 -07001173 data.setText(PhoneNumberUtils.formatNumber(entry.data));
1174 } else {
1175 data.setText(entry.data);
1176 }
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08001177 setMaxLines(data, entry.maxLines);
1178 }
1179
Jeff Sharkey624ddc32009-10-01 21:32:19 -07001180 // Set the footer
1181 if (!TextUtils.isEmpty(entry.footerLine)) {
1182 views.footer.setText(entry.footerLine);
1183 views.footer.setVisibility(View.VISIBLE);
1184 } else {
1185 views.footer.setVisibility(View.GONE);
1186 }
1187
Evan Millar15e514d2009-08-04 10:14:57 -07001188 // Set the primary icon
1189 views.primaryIcon.setVisibility(entry.isPrimary ? View.VISIBLE : View.GONE);
1190
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08001191 // Set the action icon
1192 ImageView action = views.actionIcon;
1193 if (entry.actionIcon != -1) {
Jeff Sharkeyab066932009-09-21 09:55:30 -07001194 Drawable actionIcon;
1195 if (entry.resPackageName != null) {
1196 // Load external resources through PackageManager
1197 actionIcon = mContext.getPackageManager().getDrawable(entry.resPackageName,
1198 entry.actionIcon, null);
1199 } else {
1200 actionIcon = resources.getDrawable(entry.actionIcon);
1201 }
1202 action.setImageDrawable(actionIcon);
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08001203 action.setVisibility(View.VISIBLE);
1204 } else {
1205 // Things should still line up as if there was an icon, so make it invisible
1206 action.setVisibility(View.INVISIBLE);
1207 }
1208
1209 // Set the presence icon
Bai Tao107736c2010-03-12 08:00:42 +08001210 Drawable presenceIcon = ContactPresenceIconUtil.getPresenceIcon(
1211 mContext, entry.presence);
Evan Millar15e514d2009-08-04 10:14:57 -07001212 ImageView presenceIconView = views.presenceIcon;
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08001213 if (presenceIcon != null) {
Evan Millar15e514d2009-08-04 10:14:57 -07001214 presenceIconView.setImageDrawable(presenceIcon);
1215 presenceIconView.setVisibility(View.VISIBLE);
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08001216 } else {
Evan Millar15e514d2009-08-04 10:14:57 -07001217 presenceIconView.setVisibility(View.GONE);
1218 }
1219
1220 // Set the secondary action button
1221 ImageView secondaryActionView = views.secondaryActionButton;
1222 Drawable secondaryActionIcon = null;
1223 if (entry.secondaryActionIcon != -1) {
1224 secondaryActionIcon = resources.getDrawable(entry.secondaryActionIcon);
1225 }
1226 if (entry.secondaryIntent != null && secondaryActionIcon != null) {
1227 secondaryActionView.setImageDrawable(secondaryActionIcon);
1228 secondaryActionView.setTag(entry.secondaryIntent);
1229 secondaryActionView.setVisibility(View.VISIBLE);
1230 views.secondaryActionDivider.setVisibility(View.VISIBLE);
1231 } else {
1232 secondaryActionView.setVisibility(View.GONE);
1233 views.secondaryActionDivider.setVisibility(View.GONE);
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08001234 }
1235 }
1236
1237 private void setMaxLines(TextView textView, int maxLines) {
1238 if (maxLines == 1) {
1239 textView.setSingleLine(true);
1240 textView.setEllipsize(TextUtils.TruncateAt.END);
1241 } else {
1242 textView.setSingleLine(false);
1243 textView.setMaxLines(maxLines);
1244 textView.setEllipsize(null);
1245 }
1246 }
1247 }
Jeff Sharkey624ddc32009-10-01 21:32:19 -07001248
1249 private interface StatusQuery {
1250 final String[] PROJECTION = new String[] {
1251 Data._ID,
1252 Data.STATUS,
1253 Data.STATUS_RES_PACKAGE,
1254 Data.STATUS_ICON,
1255 Data.STATUS_LABEL,
1256 Data.STATUS_TIMESTAMP,
1257 Data.PRESENCE,
1258 };
1259
1260 final int _ID = 0;
1261 }
Dmitri Plotnikov8e86b752010-02-22 17:47:57 -08001262
1263 @Override
1264 public void startSearch(String initialQuery, boolean selectInitialQuery, Bundle appSearchData,
1265 boolean globalSearch) {
1266 if (globalSearch) {
1267 super.startSearch(initialQuery, selectInitialQuery, appSearchData, globalSearch);
1268 } else {
1269 ContactsSearchManager.startSearch(this, initialQuery);
1270 }
1271 }
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08001272}