blob: a1aeca3293ba4b12e50641e8d86a203eaa4821c0 [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;
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -080049import android.net.Uri;
50import android.os.Bundle;
51import android.os.Handler;
52import android.os.RemoteException;
53import android.os.ServiceManager;
Dmitri Plotnikov4e3a0432009-09-15 13:54:40 -070054import android.provider.ContactsContract;
Dmitri Plotnikovb4491ee2009-06-15 09:31:02 -070055import android.provider.ContactsContract.AggregationExceptions;
56import android.provider.ContactsContract.CommonDataKinds;
Evan Millar11d628c2009-09-02 08:55:01 -070057import android.provider.ContactsContract.Contacts;
Evan Millar66388be2009-05-28 15:41:07 -070058import android.provider.ContactsContract.Data;
Dmitri Plotnikov39466592009-07-27 11:23:51 -070059import android.provider.ContactsContract.RawContacts;
Dmitri Plotnikov160e9da2009-09-30 23:48:12 -070060import android.provider.ContactsContract.StatusUpdates;
Jeff Sharkey624ddc32009-10-01 21:32:19 -070061import android.provider.ContactsContract.CommonDataKinds.Email;
62import android.provider.ContactsContract.CommonDataKinds.Im;
63import android.provider.ContactsContract.CommonDataKinds.Nickname;
64import android.provider.ContactsContract.CommonDataKinds.Note;
65import android.provider.ContactsContract.CommonDataKinds.Organization;
Evan Millar54a5c9f2009-06-23 17:41:09 -070066import android.provider.ContactsContract.CommonDataKinds.Phone;
Jeff Sharkey624ddc32009-10-01 21:32:19 -070067import android.provider.ContactsContract.CommonDataKinds.StructuredPostal;
Evan Millar54a5c9f2009-06-23 17:41:09 -070068import android.telephony.PhoneNumberUtils;
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -080069import android.text.TextUtils;
70import android.util.Log;
71import android.view.ContextMenu;
72import android.view.KeyEvent;
Evan Millar11d628c2009-09-02 08:55:01 -070073import android.view.LayoutInflater;
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -080074import android.view.Menu;
Jeff Sharkey49d17b32009-09-07 02:14:21 -070075import android.view.MenuInflater;
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -080076import android.view.MenuItem;
77import android.view.View;
78import android.view.ViewGroup;
Evan Millar11d628c2009-09-02 08:55:01 -070079import android.view.Window;
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -080080import android.view.ContextMenu.ContextMenuInfo;
81import android.widget.AdapterView;
Evan Millar7911ff52009-07-21 15:55:18 -070082import android.widget.FrameLayout;
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -080083import android.widget.ImageView;
84import android.widget.ListView;
Evan Millarcdeea942009-10-07 11:47:35 -070085import android.widget.ScrollView;
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -080086import android.widget.TextView;
87import android.widget.Toast;
88
89import java.util.ArrayList;
Jeff Sharkey624ddc32009-10-01 21:32:19 -070090import java.util.HashMap;
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -080091
92/**
93 * Displays the details of a specific contact.
94 */
Evan Millar11d628c2009-09-02 08:55:01 -070095public class ViewContactActivity extends Activity
Evan Millar7911ff52009-07-21 15:55:18 -070096 implements View.OnCreateContextMenuListener, DialogInterface.OnClickListener,
Evan Millardf181202009-09-30 15:15:51 -070097 AdapterView.OnItemClickListener, NotifyingAsyncQueryHandler.AsyncQueryListener {
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -080098 private static final String TAG = "ViewContact";
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -080099
100 private static final boolean SHOW_SEPARATORS = false;
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800101
102 private static final int DIALOG_CONFIRM_DELETE = 1;
Megha Joshid5afd8a2009-09-22 09:38:41 -0700103 private static final int DIALOG_CONFIRM_READONLY_DELETE = 2;
104 private static final int DIALOG_CONFIRM_MULTIPLE_DELETE = 3;
Megha Joshic508bd82009-10-01 17:31:49 -0700105 private static final int DIALOG_CONFIRM_READONLY_HIDE = 4;
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800106
Evan Millar8a79cee2009-08-19 17:20:49 -0700107 private static final int REQUEST_JOIN_CONTACT = 1;
Evan Millar9cd81242009-09-26 16:02:31 -0700108 private static final int REQUEST_EDIT_CONTACT = 2;
Dmitri Plotnikov49f705f2009-06-17 18:31:56 -0700109
Evan Millar8a79cee2009-08-19 17:20:49 -0700110 public static final int MENU_ITEM_MAKE_DEFAULT = 3;
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800111
Dmitri Plotnikov99eafe72009-09-03 14:09:45 -0700112 protected Uri mLookupUri;
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800113 private ContentResolver mResolver;
114 private ViewAdapter mAdapter;
115 private int mNumPhoneNumbers = 0;
116
Dmitri Plotnikovb4491ee2009-06-15 09:31:02 -0700117 /**
Dmitri Plotnikove1cd6792009-07-27 20:28:17 -0700118 * A list of distinct contact IDs included in the current contact.
Dmitri Plotnikovb4491ee2009-06-15 09:31:02 -0700119 */
Evan Millar7911ff52009-07-21 15:55:18 -0700120 private ArrayList<Long> mRawContactIds = new ArrayList<Long>();
Dmitri Plotnikovb4491ee2009-06-15 09:31:02 -0700121
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800122 /* package */ ArrayList<ViewEntry> mPhoneEntries = new ArrayList<ViewEntry>();
123 /* package */ ArrayList<ViewEntry> mSmsEntries = new ArrayList<ViewEntry>();
124 /* package */ ArrayList<ViewEntry> mEmailEntries = new ArrayList<ViewEntry>();
125 /* package */ ArrayList<ViewEntry> mPostalEntries = new ArrayList<ViewEntry>();
126 /* package */ ArrayList<ViewEntry> mImEntries = new ArrayList<ViewEntry>();
127 /* package */ ArrayList<ViewEntry> mOrganizationEntries = new ArrayList<ViewEntry>();
The Android Open Source Projectcac191e2009-03-18 22:20:27 -0700128 /* package */ ArrayList<ViewEntry> mGroupEntries = new ArrayList<ViewEntry>();
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800129 /* package */ ArrayList<ViewEntry> mOtherEntries = new ArrayList<ViewEntry>();
130 /* package */ ArrayList<ArrayList<ViewEntry>> mSections = new ArrayList<ArrayList<ViewEntry>>();
131
132 private Cursor mCursor;
Evan Millar5c22c3b2009-05-29 11:37:54 -0700133
Evan Millar11d628c2009-09-02 08:55:01 -0700134 protected ContactHeaderWidget mContactHeaderWidget;
135 private NotifyingAsyncQueryHandler mHandler;
136
137 protected LayoutInflater mInflater;
138
Megha Joshid5afd8a2009-09-22 09:38:41 -0700139 protected int mReadOnlySourcesCnt;
140 protected int mWritableSourcesCnt;
141 protected ArrayList<Long> mWritableRawContactIds = new ArrayList<Long>();
Evan Millar11d628c2009-09-02 08:55:01 -0700142
Jeff Sharkey624ddc32009-10-01 21:32:19 -0700143 private static final int TOKEN_ENTITIES = 0;
144 private static final int TOKEN_STATUSES = 1;
145
146 private boolean mHasEntities = false;
147 private boolean mHasStatuses = false;
148
149 private ArrayList<Entity> mEntities = Lists.newArrayList();
150 private HashMap<Long, DataStatus> mStatuses = Maps.newHashMap();
Evan Millar11d628c2009-09-02 08:55:01 -0700151
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800152 private ContentObserver mObserver = new ContentObserver(new Handler()) {
153 @Override
154 public boolean deliverSelfNotifications() {
155 return true;
156 }
157
158 @Override
159 public void onChange(boolean selfChange) {
Evan Millar11d628c2009-09-02 08:55:01 -0700160 if (mCursor != null && !mCursor.isClosed()) {
161 startEntityQuery();
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800162 }
163 }
164 };
165
166 public void onClick(DialogInterface dialog, int which) {
Dmitri Plotnikov99eafe72009-09-03 14:09:45 -0700167 closeCursor();
Megha Joshic508bd82009-10-01 17:31:49 -0700168 getContentResolver().delete(mLookupUri, null, null);
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800169 finish();
170 }
171
Evan Millar7911ff52009-07-21 15:55:18 -0700172 private ListView mListView;
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800173 private boolean mShowSmsLinksForAllPhones;
174
175 @Override
176 protected void onCreate(Bundle icicle) {
177 super.onCreate(icicle);
178
Evan Millar11d628c2009-09-02 08:55:01 -0700179 final Intent intent = getIntent();
Dmitri Plotnikov4e3a0432009-09-15 13:54:40 -0700180 Uri data = intent.getData();
181 String authority = data.getAuthority();
182 if (ContactsContract.AUTHORITY.equals(authority)) {
183 mLookupUri = data;
184 } else if (android.provider.Contacts.AUTHORITY.equals(authority)) {
185 final long rawContactId = ContentUris.parseId(data);
186 mLookupUri = RawContacts.getContactLookupUri(getContentResolver(),
187 ContentUris.withAppendedId(RawContacts.CONTENT_URI, rawContactId));
Dmitri Plotnikov83129f02009-09-02 19:04:41 -0700188
Dmitri Plotnikov4e3a0432009-09-15 13:54:40 -0700189 }
Dmitri Plotnikov83129f02009-09-02 19:04:41 -0700190 mInflater = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE);
Evan Millar11d628c2009-09-02 08:55:01 -0700191
192 requestWindowFeature(Window.FEATURE_NO_TITLE);
193 setContentView(R.layout.contact_card_layout);
194
195 mContactHeaderWidget = (ContactHeaderWidget) findViewById(R.id.contact_header_widget);
196 mContactHeaderWidget.showStar(true);
Evan Millar11d628c2009-09-02 08:55:01 -0700197 mContactHeaderWidget.setExcludeMimes(new String[] {
198 Contacts.CONTENT_ITEM_TYPE
199 });
200
Evan Millar11d628c2009-09-02 08:55:01 -0700201 mHandler = new NotifyingAsyncQueryHandler(this, this);
202
Evan Millarcdeea942009-10-07 11:47:35 -0700203 mListView = (ListView) findViewById(R.id.contact_data);
Evan Millar7911ff52009-07-21 15:55:18 -0700204 mListView.setOnCreateContextMenuListener(this);
205 mListView.setScrollBarStyle(ListView.SCROLLBARS_OUTSIDE_OVERLAY);
206 mListView.setOnItemClickListener(this);
Evan Millarcdeea942009-10-07 11:47:35 -0700207 mListView.setEmptyView((ScrollView) findViewById(android.R.id.empty));
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800208
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800209 mResolver = getContentResolver();
210
211 // Build the list of sections. The order they're added to mSections dictates the
212 // order they are displayed in the list.
213 mSections.add(mPhoneEntries);
214 mSections.add(mSmsEntries);
215 mSections.add(mEmailEntries);
216 mSections.add(mImEntries);
217 mSections.add(mPostalEntries);
218 mSections.add(mOrganizationEntries);
The Android Open Source Projectcac191e2009-03-18 22:20:27 -0700219 mSections.add(mGroupEntries);
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800220 mSections.add(mOtherEntries);
221
222 //TODO Read this value from a preference
223 mShowSmsLinksForAllPhones = true;
Evan Millar11d628c2009-09-02 08:55:01 -0700224 }
225
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800226 @Override
227 protected void onResume() {
228 super.onResume();
Evan Millar11d628c2009-09-02 08:55:01 -0700229 startEntityQuery();
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800230 }
231
232 @Override
233 protected void onPause() {
234 super.onPause();
Dmitri Plotnikov99eafe72009-09-03 14:09:45 -0700235 closeCursor();
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800236 }
237
238 @Override
239 protected void onDestroy() {
240 super.onDestroy();
Dmitri Plotnikov99eafe72009-09-03 14:09:45 -0700241 closeCursor();
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800242 }
243
244 @Override
245 protected Dialog onCreateDialog(int id) {
246 switch (id) {
247 case DIALOG_CONFIRM_DELETE:
248 return new AlertDialog.Builder(this)
249 .setTitle(R.string.deleteConfirmation_title)
250 .setIcon(android.R.drawable.ic_dialog_alert)
251 .setMessage(R.string.deleteConfirmation)
252 .setNegativeButton(android.R.string.cancel, null)
253 .setPositiveButton(android.R.string.ok, this)
254 .setCancelable(false)
255 .create();
Megha Joshid5afd8a2009-09-22 09:38:41 -0700256 case DIALOG_CONFIRM_READONLY_DELETE:
257 return new AlertDialog.Builder(this)
258 .setTitle(R.string.deleteConfirmation_title)
259 .setIcon(android.R.drawable.ic_dialog_alert)
260 .setMessage(R.string.readOnlyContactDeleteConfirmation)
261 .setNegativeButton(android.R.string.cancel, null)
262 .setPositiveButton(android.R.string.ok, this)
263 .setCancelable(false)
264 .create();
265 case DIALOG_CONFIRM_MULTIPLE_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.multipleContactDeleteConfirmation)
270 .setNegativeButton(android.R.string.cancel, null)
271 .setPositiveButton(android.R.string.ok, this)
272 .setCancelable(false)
273 .create();
Evan Millarcdeea942009-10-07 11:47:35 -0700274 case DIALOG_CONFIRM_READONLY_HIDE: {
Megha Joshic508bd82009-10-01 17:31:49 -0700275 return new AlertDialog.Builder(this)
276 .setTitle(R.string.deleteConfirmation_title)
277 .setIcon(android.R.drawable.ic_dialog_alert)
278 .setMessage(R.string.readOnlyContactWarning)
279 .setPositiveButton(android.R.string.ok, this)
Evan Millarcdeea942009-10-07 11:47:35 -0700280 .create();
Megha Joshic508bd82009-10-01 17:31:49 -0700281 }
282
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800283 }
284 return null;
285 }
286
Evan Millar11d628c2009-09-02 08:55:01 -0700287 // QUERY CODE //
288 /** {@inheritDoc} */
289 public void onQueryEntitiesComplete(int token, Object cookie, EntityIterator iterator) {
Jeff Sharkey624ddc32009-10-01 21:32:19 -0700290 try {
291 // Read incoming entities and consider binding
292 readEntities(iterator);
293 considerBindData();
Evan Millar11d628c2009-09-02 08:55:01 -0700294 } finally {
295 if (iterator != null) {
296 iterator.close();
297 }
298 }
299 }
300
301 /** {@inheritDoc} */
302 public void onQueryComplete(int token, Object cookie, Cursor cursor) {
Jeff Sharkey624ddc32009-10-01 21:32:19 -0700303 try {
304 // Read available social rows and consider binding
305 readStatuses(cursor);
306 considerBindData();
307 } finally {
308 if (cursor != null) {
309 cursor.close();
310 }
311 }
Evan Millar11d628c2009-09-02 08:55:01 -0700312 }
313
Evan Millar6a61a1a2009-09-29 14:00:43 -0700314 private long getRefreshedContactId() {
315 Uri freshContactUri = Contacts.lookupContact(getContentResolver(), mLookupUri);
316 if (freshContactUri != null) {
317 return ContentUris.parseId(freshContactUri);
318 }
319 return -1;
320 }
321
Jeff Sharkey624ddc32009-10-01 21:32:19 -0700322 /**
323 * Read from the given {@link EntityIterator} to build internal set of
324 * {@link #mEntities} for data display.
325 */
326 private synchronized void readEntities(EntityIterator iterator) {
327 mEntities.clear();
Evan Millar11d628c2009-09-02 08:55:01 -0700328 try {
329 while (iterator.hasNext()) {
Jeff Sharkey624ddc32009-10-01 21:32:19 -0700330 mEntities.add(iterator.next());
Evan Millar11d628c2009-09-02 08:55:01 -0700331 }
Jeff Sharkey624ddc32009-10-01 21:32:19 -0700332 mHasEntities = true;
Evan Millar11d628c2009-09-02 08:55:01 -0700333 } catch (RemoteException e) {
Jeff Sharkey624ddc32009-10-01 21:32:19 -0700334 Log.w(TAG, "Problem reading contact data: " + e.toString());
Evan Millar11d628c2009-09-02 08:55:01 -0700335 }
Evan Millar11d628c2009-09-02 08:55:01 -0700336 }
337
Jeff Sharkey624ddc32009-10-01 21:32:19 -0700338 /**
339 * Read from the given {@link Cursor} and build a set of {@link DataStatus}
340 * objects to match any valid statuses found.
341 */
342 private synchronized void readStatuses(Cursor cursor) {
343 mStatuses.clear();
344
345 // Walk found statuses, creating internal row for each
346 while (cursor.moveToNext()) {
347 final DataStatus status = new DataStatus(cursor);
348 final long dataId = cursor.getLong(StatusQuery._ID);
349 mStatuses.put(dataId, status);
350 }
351
352 mHasStatuses = true;
353 }
354
355 private synchronized void startEntityQuery() {
Dmitri Plotnikov99eafe72009-09-03 14:09:45 -0700356 closeCursor();
357
Evan Millar6a61a1a2009-09-29 14:00:43 -0700358 Uri uri = null;
Dmitri Plotnikov99eafe72009-09-03 14:09:45 -0700359 if (mLookupUri != null) {
360 mLookupUri = Contacts.getLookupUri(getContentResolver(), mLookupUri);
361 if (mLookupUri != null) {
Evan Millar6a61a1a2009-09-29 14:00:43 -0700362 uri = Contacts.lookupContact(getContentResolver(), mLookupUri);
Dmitri Plotnikov99eafe72009-09-03 14:09:45 -0700363 }
364 }
365
Evan Millar6a61a1a2009-09-29 14:00:43 -0700366 if (uri == null) {
Dmitri Plotnikov99eafe72009-09-03 14:09:45 -0700367
368 // TODO either figure out a way to prevent a flash of black background or
369 // use some other UI than a toast
370 Toast.makeText(this, R.string.invalidContactMessage, Toast.LENGTH_SHORT).show();
371 Log.e(TAG, "invalid contact uri: " + mLookupUri);
372 finish();
373 return;
374 }
375
Jeff Sharkey624ddc32009-10-01 21:32:19 -0700376 final Uri dataUri = Uri.withAppendedPath(uri, Contacts.Data.CONTENT_DIRECTORY);
377
378 // Keep stub cursor open on side to watch for change events
379 mCursor = mResolver.query(dataUri,
Dmitri Plotnikov99eafe72009-09-03 14:09:45 -0700380 new String[] {Contacts.DISPLAY_NAME}, null, null, null);
381 mCursor.registerContentObserver(mObserver);
382
Jeff Sharkey624ddc32009-10-01 21:32:19 -0700383 final long contactId = ContentUris.parseId(uri);
384
385 // Clear flags and start queries to data and status
386 mHasEntities = false;
387 mHasStatuses = false;
388
389 mHandler.startQueryEntities(TOKEN_ENTITIES, null, RawContacts.CONTENT_URI,
390 RawContacts.CONTACT_ID + "=" + contactId, null, null);
391 mHandler.startQuery(TOKEN_STATUSES, null, dataUri, StatusQuery.PROJECTION,
392 StatusUpdates.PRESENCE + " IS NOT NULL OR " + StatusUpdates.STATUS
393 + " IS NOT NULL", null, null);
Dmitri Plotnikov99eafe72009-09-03 14:09:45 -0700394
Evan Millar9b1a1242009-09-16 10:26:10 -0700395 mContactHeaderWidget.bindFromContactLookupUri(mLookupUri);
Dmitri Plotnikov99eafe72009-09-03 14:09:45 -0700396 }
397
398 private void closeCursor() {
399 if (mCursor != null) {
400 mCursor.unregisterContentObserver(mObserver);
401 mCursor.close();
402 mCursor = null;
403 }
Evan Millar11d628c2009-09-02 08:55:01 -0700404 }
405
Jeff Sharkey624ddc32009-10-01 21:32:19 -0700406 /**
407 * Consider binding views after any of several background queries has
408 * completed. We check internal flags and only bind when all data has
409 * arrived.
410 */
411 private void considerBindData() {
412 if (mHasEntities && mHasStatuses) {
413 bindData();
414 }
415 }
416
Evan Millar11d628c2009-09-02 08:55:01 -0700417 private void bindData() {
418
419 // Build up the contact entries
420 buildEntries();
421
422 // Collapse similar data items in select sections.
423 Collapser.collapseList(mPhoneEntries);
424 Collapser.collapseList(mSmsEntries);
425 Collapser.collapseList(mEmailEntries);
426 Collapser.collapseList(mPostalEntries);
Jeff Sharkey624ddc32009-10-01 21:32:19 -0700427 Collapser.collapseList(mImEntries);
Evan Millar11d628c2009-09-02 08:55:01 -0700428
429 if (mAdapter == null) {
430 mAdapter = new ViewAdapter(this, mSections);
431 mListView.setAdapter(mAdapter);
432 } else {
433 mAdapter.setSections(mSections, SHOW_SEPARATORS);
434 }
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800435 }
436
437 @Override
438 public boolean onCreateOptionsMenu(Menu menu) {
Jeff Sharkey49d17b32009-09-07 02:14:21 -0700439 super.onCreateOptionsMenu(menu);
440
441 final MenuInflater inflater = getMenuInflater();
442 inflater.inflate(R.menu.view, menu);
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800443 return true;
444 }
445
446 @Override
447 public boolean onPrepareOptionsMenu(Menu menu) {
448 super.onPrepareOptionsMenu(menu);
Dmitri Plotnikovb4491ee2009-06-15 09:31:02 -0700449
Evan Millar51514db2009-09-22 11:42:35 -0700450 // Only allow edit when we have at least one raw_contact id
451 final boolean hasRawContact = (mRawContactIds.size() > 0);
Evan Millar2e1b8af2009-09-24 09:39:21 -0700452 menu.findItem(R.id.menu_edit).setEnabled(hasRawContact);
Jeff Sharkey49d17b32009-09-07 02:14:21 -0700453
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800454 return true;
455 }
456
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800457 @Override
458 public void onCreateContextMenu(ContextMenu menu, View view, ContextMenuInfo menuInfo) {
459 AdapterView.AdapterContextMenuInfo info;
460 try {
461 info = (AdapterView.AdapterContextMenuInfo) menuInfo;
462 } catch (ClassCastException e) {
463 Log.e(TAG, "bad menuInfo", e);
464 return;
465 }
466
467 // This can be null sometimes, don't crash...
468 if (info == null) {
469 Log.e(TAG, "bad menuInfo");
470 return;
471 }
Evan Millar5c22c3b2009-05-29 11:37:54 -0700472
Evan Millar45e0ed32009-06-01 16:44:38 -0700473 ViewEntry entry = ContactEntryAdapter.getEntry(mSections, info.position, SHOW_SEPARATORS);
Neel Parekha8fb68a2009-09-25 18:05:18 -0700474 menu.setHeaderTitle(R.string.contactOptionsTitle);
Evan Millar45e0ed32009-06-01 16:44:38 -0700475 if (entry.mimetype.equals(CommonDataKinds.Phone.CONTENT_ITEM_TYPE)) {
476 menu.add(0, 0, 0, R.string.menu_call).setIntent(entry.intent);
Evan Millar15e514d2009-08-04 10:14:57 -0700477 menu.add(0, 0, 0, R.string.menu_sendSMS).setIntent(entry.secondaryIntent);
478 if (!entry.isPrimary) {
Evan Millar45e0ed32009-06-01 16:44:38 -0700479 menu.add(0, MENU_ITEM_MAKE_DEFAULT, 0, R.string.menu_makeDefaultNumber);
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800480 }
Evan Millar45e0ed32009-06-01 16:44:38 -0700481 } else if (entry.mimetype.equals(CommonDataKinds.Email.CONTENT_ITEM_TYPE)) {
482 menu.add(0, 0, 0, R.string.menu_sendEmail).setIntent(entry.intent);
Evan Millar15e514d2009-08-04 10:14:57 -0700483 if (!entry.isPrimary) {
Evan Millar45e0ed32009-06-01 16:44:38 -0700484 menu.add(0, MENU_ITEM_MAKE_DEFAULT, 0, R.string.menu_makeDefaultEmail);
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800485 }
Jeff Sharkeyc6ad3ab2009-07-21 19:30:15 -0700486 } else if (entry.mimetype.equals(CommonDataKinds.StructuredPostal.CONTENT_ITEM_TYPE)) {
Evan Millar45e0ed32009-06-01 16:44:38 -0700487 menu.add(0, 0, 0, R.string.menu_viewAddress).setIntent(entry.intent);
488 }
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800489 }
490
491 @Override
492 public boolean onOptionsItemSelected(MenuItem item) {
493 switch (item.getItemId()) {
Jeff Sharkey49d17b32009-09-07 02:14:21 -0700494 case R.id.menu_edit: {
Evan Millardf181202009-09-30 15:15:51 -0700495 Long rawContactIdToEdit = null;
496 if (mRawContactIds.size() > 0) {
497 rawContactIdToEdit = mRawContactIds.get(0);
498 } else {
499 // There is no rawContact to edit.
500 break;
Evan Millardb5d88c2009-08-28 09:31:57 -0700501 }
Evan Millar2e1b8af2009-09-24 09:39:21 -0700502 Uri rawContactUri = ContentUris.withAppendedId(RawContacts.CONTENT_URI,
503 rawContactIdToEdit);
Evan Millar9cd81242009-09-26 16:02:31 -0700504 startActivityForResult(new Intent(Intent.ACTION_EDIT, rawContactUri),
505 REQUEST_EDIT_CONTACT);
Evan Millar8a79cee2009-08-19 17:20:49 -0700506 break;
507 }
Jeff Sharkey49d17b32009-09-07 02:14:21 -0700508 case R.id.menu_delete: {
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800509 // Get confirmation
Evan Millarcdeea942009-10-07 11:47:35 -0700510 if (mReadOnlySourcesCnt > 0 & mWritableSourcesCnt > 0) {
Megha Joshid5afd8a2009-09-22 09:38:41 -0700511 showDialog(DIALOG_CONFIRM_READONLY_DELETE);
Evan Millarcdeea942009-10-07 11:47:35 -0700512 } else if (mReadOnlySourcesCnt > 0 && mWritableSourcesCnt == 0) {
Megha Joshic508bd82009-10-01 17:31:49 -0700513 showDialog(DIALOG_CONFIRM_READONLY_HIDE);
Evan Millarcdeea942009-10-07 11:47:35 -0700514 } else if (mReadOnlySourcesCnt == 0 && mWritableSourcesCnt > 1) {
515 showDialog(DIALOG_CONFIRM_MULTIPLE_DELETE);
516 } else {
517 showDialog(DIALOG_CONFIRM_DELETE);
518 }
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800519 return true;
520 }
Jeff Sharkey49d17b32009-09-07 02:14:21 -0700521 case R.id.menu_join: {
Dmitri Plotnikov49f705f2009-06-17 18:31:56 -0700522 showJoinAggregateActivity();
523 return true;
524 }
Jeff Sharkey49d17b32009-09-07 02:14:21 -0700525 case R.id.menu_options: {
Dmitri Plotnikovef038722009-06-24 18:51:47 -0700526 showOptionsActivity();
527 return true;
528 }
Jeff Sharkey49d17b32009-09-07 02:14:21 -0700529 case R.id.menu_share: {
Jeff Sharkey93049312009-09-27 17:28:13 -0700530 // TODO: Keep around actual LOOKUP_KEY, or formalize method of extracting
531 final String lookupKey = mLookupUri.getPathSegments().get(2);
532 final Uri shareUri = Uri.withAppendedPath(Contacts.CONTENT_VCARD_URI, lookupKey);
533
Jeff Sharkey49d17b32009-09-07 02:14:21 -0700534 final Intent intent = new Intent(Intent.ACTION_SEND);
Jeff Sharkey93049312009-09-27 17:28:13 -0700535 intent.setType(Contacts.CONTENT_VCARD_TYPE);
536 intent.putExtra(Intent.EXTRA_STREAM, shareUri);
Dmitri Plotnikovef038722009-06-24 18:51:47 -0700537
Jeff Sharkey49d17b32009-09-07 02:14:21 -0700538 // Launch chooser to share contact via
539 final CharSequence chooseTitle = getText(R.string.share_via);
540 final Intent chooseIntent = Intent.createChooser(intent, chooseTitle);
Evan Millar5c22c3b2009-05-29 11:37:54 -0700541
Jeff Sharkey49d17b32009-09-07 02:14:21 -0700542 try {
543 startActivity(chooseIntent);
544 } catch (ActivityNotFoundException ex) {
545 Toast.makeText(this, R.string.share_error, Toast.LENGTH_SHORT).show();
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800546 }
Jeff Sharkey49d17b32009-09-07 02:14:21 -0700547 return true;
548 }
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800549 }
550 return super.onOptionsItemSelected(item);
551 }
Evan Millar5c22c3b2009-05-29 11:37:54 -0700552
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800553 @Override
554 public boolean onContextItemSelected(MenuItem item) {
555 switch (item.getItemId()) {
556 case MENU_ITEM_MAKE_DEFAULT: {
Dmitri Plotnikovb4491ee2009-06-15 09:31:02 -0700557 if (makeItemDefault(item)) {
558 return true;
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800559 }
Dmitri Plotnikovb4491ee2009-06-15 09:31:02 -0700560 break;
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800561 }
562 }
Dmitri Plotnikovb4491ee2009-06-15 09:31:02 -0700563
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800564 return super.onContextItemSelected(item);
565 }
566
Dmitri Plotnikovb4491ee2009-06-15 09:31:02 -0700567 private boolean makeItemDefault(MenuItem item) {
568 ViewEntry entry = getViewEntryForMenuItem(item);
569 if (entry == null) {
570 return false;
571 }
572
573 // Update the primary values in the data record.
Evan Millar14fecb62009-09-09 09:23:12 -0700574 ContentValues values = new ContentValues(1);
Evan Millar54a5c9f2009-06-23 17:41:09 -0700575 values.put(Data.IS_SUPER_PRIMARY, 1);
Dmitri Plotnikovb4491ee2009-06-15 09:31:02 -0700576 getContentResolver().update(ContentUris.withAppendedId(Data.CONTENT_URI, entry.id),
577 values, null, null);
Evan Millar11d628c2009-09-02 08:55:01 -0700578 startEntityQuery();
Dmitri Plotnikovb4491ee2009-06-15 09:31:02 -0700579 return true;
580 }
581
582 /**
Dmitri Plotnikov49f705f2009-06-17 18:31:56 -0700583 * Shows a list of aggregates that can be joined into the currently viewed aggregate.
Dmitri Plotnikovb4491ee2009-06-15 09:31:02 -0700584 */
Dmitri Plotnikov49f705f2009-06-17 18:31:56 -0700585 public void showJoinAggregateActivity() {
Evan Millar6a61a1a2009-09-29 14:00:43 -0700586 long freshId = getRefreshedContactId();
587 if (freshId > 0) {
588 String displayName = null;
589 if (mCursor.moveToFirst()) {
590 displayName = mCursor.getString(0);
591 }
592 Intent intent = new Intent(ContactsListActivity.JOIN_AGGREGATE);
593 intent.putExtra(ContactsListActivity.EXTRA_AGGREGATE_ID, freshId);
594 if (displayName != null) {
595 intent.putExtra(ContactsListActivity.EXTRA_AGGREGATE_NAME, displayName);
596 }
597 startActivityForResult(intent, REQUEST_JOIN_CONTACT);
Dmitri Plotnikova5cfca32009-09-29 18:13:08 -0700598 }
Dmitri Plotnikov49f705f2009-06-17 18:31:56 -0700599 }
Dmitri Plotnikovb4491ee2009-06-15 09:31:02 -0700600
Dmitri Plotnikov49f705f2009-06-17 18:31:56 -0700601 @Override
602 protected void onActivityResult(int requestCode, int resultCode, Intent intent) {
Evan Millar2e1b8af2009-09-24 09:39:21 -0700603 if (requestCode == REQUEST_JOIN_CONTACT) {
604 if (resultCode == RESULT_OK && intent != null) {
605 final long contactId = ContentUris.parseId(intent.getData());
606 joinAggregate(contactId);
Evan Millar8a79cee2009-08-19 17:20:49 -0700607 }
Evan Millar9cd81242009-09-26 16:02:31 -0700608 } else if (requestCode == REQUEST_EDIT_CONTACT) {
Evan Millardf181202009-09-30 15:15:51 -0700609 if (resultCode == EditContactActivity.RESULT_CLOSE_VIEW_ACTIVITY) {
610 finish();
Jeff Hamilton5297c6a2009-10-01 02:22:33 -0700611 } else if (resultCode == Activity.RESULT_OK) {
Dmitri Plotnikov160e9da2009-09-30 23:48:12 -0700612 mLookupUri = intent.getData();
613 if (mLookupUri == null) {
614 finish();
615 }
Evan Millardf181202009-09-30 15:15:51 -0700616 }
Dmitri Plotnikov49f705f2009-06-17 18:31:56 -0700617 }
618 }
619
Dmitri Plotnikov99eafe72009-09-03 14:09:45 -0700620 private void splitContact(long rawContactId) {
Dmitri Plotnikovf40ccdf2009-09-04 17:37:15 -0700621 setAggregationException(rawContactId, AggregationExceptions.TYPE_KEEP_SEPARATE);
Dmitri Plotnikov99eafe72009-09-03 14:09:45 -0700622
623 // The split operation may have removed the original aggregate contact, so we need
624 // to requery everything
Dmitri Plotnikov040dc152009-09-03 15:17:56 -0700625 Toast.makeText(this, R.string.contactsSplitMessage, Toast.LENGTH_LONG).show();
Dmitri Plotnikov99eafe72009-09-03 14:09:45 -0700626 startEntityQuery();
Dmitri Plotnikov49f705f2009-06-17 18:31:56 -0700627 }
628
Dmitri Plotnikovf40ccdf2009-09-04 17:37:15 -0700629 private void joinAggregate(final long contactId) {
Dmitri Plotnikov39466592009-07-27 11:23:51 -0700630 Cursor c = mResolver.query(RawContacts.CONTENT_URI, new String[] {RawContacts._ID},
Dmitri Plotnikovf40ccdf2009-09-04 17:37:15 -0700631 RawContacts.CONTACT_ID + "=" + contactId, null, null);
Dmitri Plotnikov49f705f2009-06-17 18:31:56 -0700632
633 try {
634 while(c.moveToNext()) {
Dmitri Plotnikovf40ccdf2009-09-04 17:37:15 -0700635 long rawContactId = c.getLong(0);
636 setAggregationException(rawContactId, AggregationExceptions.TYPE_KEEP_TOGETHER);
Dmitri Plotnikov49f705f2009-06-17 18:31:56 -0700637 }
638 } finally {
639 c.close();
640 }
641
Dmitri Plotnikov040dc152009-09-03 15:17:56 -0700642 Toast.makeText(this, R.string.contactsJoinedMessage, Toast.LENGTH_LONG).show();
643 startEntityQuery();
Dmitri Plotnikov49f705f2009-06-17 18:31:56 -0700644 }
645
646 /**
647 * Given a contact ID sets an aggregation exception to either join the contact with the
648 * current aggregate or split off.
649 */
Dmitri Plotnikov99eafe72009-09-03 14:09:45 -0700650 protected void setAggregationException(long rawContactId, int exceptionType) {
Dmitri Plotnikovd09f75c2009-06-16 11:59:22 -0700651 ContentValues values = new ContentValues(3);
Dmitri Plotnikovf40ccdf2009-09-04 17:37:15 -0700652 for (long aRawContactId : mRawContactIds) {
653 if (aRawContactId != rawContactId) {
654 values.put(AggregationExceptions.RAW_CONTACT_ID1, aRawContactId);
655 values.put(AggregationExceptions.RAW_CONTACT_ID2, rawContactId);
656 values.put(AggregationExceptions.TYPE, exceptionType);
657 mResolver.update(AggregationExceptions.CONTENT_URI, values, null, null);
658 }
659 }
Dmitri Plotnikovb4491ee2009-06-15 09:31:02 -0700660 }
661
Dmitri Plotnikovef038722009-06-24 18:51:47 -0700662 private void showOptionsActivity() {
663 final Intent intent = new Intent(this, ContactOptionsActivity.class);
Evan Millar6a61a1a2009-09-29 14:00:43 -0700664 intent.setData(mLookupUri);
Dmitri Plotnikovef038722009-06-24 18:51:47 -0700665 startActivity(intent);
666 }
667
Dmitri Plotnikovb4491ee2009-06-15 09:31:02 -0700668 private ViewEntry getViewEntryForMenuItem(MenuItem item) {
669 AdapterView.AdapterContextMenuInfo info;
670 try {
671 info = (AdapterView.AdapterContextMenuInfo) item.getMenuInfo();
672 } catch (ClassCastException e) {
673 Log.e(TAG, "bad menuInfo", e);
674 return null;
675 }
676
677 return ContactEntryAdapter.getEntry(mSections, info.position, SHOW_SEPARATORS);
678 }
679
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800680 @Override
681 public boolean onKeyDown(int keyCode, KeyEvent event) {
682 switch (keyCode) {
683 case KeyEvent.KEYCODE_CALL: {
684 try {
685 ITelephony phone = ITelephony.Stub.asInterface(
686 ServiceManager.checkService("phone"));
687 if (phone != null && !phone.isIdle()) {
688 // Skip out and let the key be handled at a higher level
689 break;
690 }
691 } catch (RemoteException re) {
692 // Fall through and try to call the contact
693 }
694
Evan Millar7911ff52009-07-21 15:55:18 -0700695 int index = mListView.getSelectedItemPosition();
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800696 if (index != -1) {
697 ViewEntry entry = ViewAdapter.getEntry(mSections, index, SHOW_SEPARATORS);
Evan Millar66388be2009-05-28 15:41:07 -0700698 if (entry.intent.getAction() == Intent.ACTION_CALL_PRIVILEGED) {
699 startActivity(entry.intent);
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800700 }
701 } else if (mNumPhoneNumbers != 0) {
702 // There isn't anything selected, call the default number
Evan Millar6a61a1a2009-09-29 14:00:43 -0700703 long freshContactId = getRefreshedContactId();
704 if (freshContactId > 0) {
705 Uri hardContacUri = ContentUris.withAppendedId(
706 Contacts.CONTENT_URI, freshContactId);
707 Intent intent = new Intent(Intent.ACTION_CALL_PRIVILEGED, hardContacUri);
708 startActivity(intent);
709 }
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800710 }
711 return true;
712 }
713
714 case KeyEvent.KEYCODE_DEL: {
Evan Millarcdeea942009-10-07 11:47:35 -0700715 if (mReadOnlySourcesCnt > 0 & mWritableSourcesCnt > 0) {
Megha Joshid5afd8a2009-09-22 09:38:41 -0700716 showDialog(DIALOG_CONFIRM_READONLY_DELETE);
Evan Millarcdeea942009-10-07 11:47:35 -0700717 } else if (mReadOnlySourcesCnt > 0 && mWritableSourcesCnt == 0) {
Megha Joshic508bd82009-10-01 17:31:49 -0700718 showDialog(DIALOG_CONFIRM_READONLY_HIDE);
Evan Millarcdeea942009-10-07 11:47:35 -0700719 } else if (mReadOnlySourcesCnt == 0 && mWritableSourcesCnt > 1) {
720 showDialog(DIALOG_CONFIRM_MULTIPLE_DELETE);
721 } else {
722 showDialog(DIALOG_CONFIRM_DELETE);
723 }
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800724 return true;
725 }
726 }
727
728 return super.onKeyDown(keyCode, event);
729 }
730
Evan Millar7911ff52009-07-21 15:55:18 -0700731 public void onItemClick(AdapterView parent, View v, int position, long id) {
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800732 ViewEntry entry = ViewAdapter.getEntry(mSections, position, SHOW_SEPARATORS);
733 if (entry != null) {
734 Intent intent = entry.intent;
Evan Millardf181202009-09-30 15:15:51 -0700735 if (intent != null) {
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800736 try {
737 startActivity(intent);
738 } catch (ActivityNotFoundException e) {
739 Log.e(TAG, "No activity found for intent: " + intent);
740 signalError();
741 }
742 } else {
743 signalError();
744 }
745 } else {
746 signalError();
747 }
748 }
749
750 /**
751 * Signal an error to the user via a beep, or some other method.
752 */
753 private void signalError() {
754 //TODO: implement this when we have the sonification APIs
755 }
756
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800757 /**
758 * Build up the entries to display on the screen.
Evan Millar5c22c3b2009-05-29 11:37:54 -0700759 *
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800760 * @param personCursor the URI for the contact being displayed
761 */
Evan Millar11d628c2009-09-02 08:55:01 -0700762 private final void buildEntries() {
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800763 // Clear out the old entries
764 final int numSections = mSections.size();
765 for (int i = 0; i < numSections; i++) {
766 mSections.get(i).clear();
767 }
768
Evan Millar7911ff52009-07-21 15:55:18 -0700769 mRawContactIds.clear();
Megha Joshid5afd8a2009-09-22 09:38:41 -0700770 mReadOnlySourcesCnt = 0;
Jeff Sharkey624ddc32009-10-01 21:32:19 -0700771 mWritableSourcesCnt = 0;
Megha Joshid5afd8a2009-09-22 09:38:41 -0700772 mWritableRawContactIds.clear();
Dmitri Plotnikovb4491ee2009-06-15 09:31:02 -0700773
Jeff Sharkey624ddc32009-10-01 21:32:19 -0700774 final Context context = this;
775 final Sources sources = Sources.getInstance(context);
Evan Millar11d628c2009-09-02 08:55:01 -0700776
Evan Millar66388be2009-05-28 15:41:07 -0700777 // Build up method entries
Evan Millar6a61a1a2009-09-29 14:00:43 -0700778 if (mLookupUri != null) {
Evan Millar11d628c2009-09-02 08:55:01 -0700779 for (Entity entity: mEntities) {
780 final ContentValues entValues = entity.getEntityValues();
781 final String accountType = entValues.getAsString(RawContacts.ACCOUNT_TYPE);
Jeff Sharkeyab066932009-09-21 09:55:30 -0700782 final long rawContactId = entValues.getAsLong(RawContacts._ID);
Evan Millar7911ff52009-07-21 15:55:18 -0700783
Dmitri Plotnikov2a8cef02009-09-23 19:01:15 -0700784 if (!mRawContactIds.contains(rawContactId)) {
785 mRawContactIds.add(rawContactId);
786 }
Megha Joshic508bd82009-10-01 17:31:49 -0700787 ContactsSource contactsSource = sources.getInflatedSource(accountType,
788 ContactsSource.LEVEL_SUMMARY);
789 if (contactsSource != null && contactsSource.readOnly) {
790 mReadOnlySourcesCnt += 1;
791 } else {
792 mWritableSourcesCnt += 1;
Evan Millarcdeea942009-10-07 11:47:35 -0700793 mWritableRawContactIds.add(rawContactId);
794 }
Megha Joshic508bd82009-10-01 17:31:49 -0700795
Dmitri Plotnikov2a8cef02009-09-23 19:01:15 -0700796
Evan Millar11d628c2009-09-02 08:55:01 -0700797 for (NamedContentValues subValue : entity.getSubValues()) {
Jeff Sharkeyab066932009-09-21 09:55:30 -0700798 final ContentValues entryValues = subValue.values;
799 entryValues.put(Data.RAW_CONTACT_ID, rawContactId);
800
Jeff Sharkey624ddc32009-10-01 21:32:19 -0700801 final long dataId = entryValues.getAsLong(Data._ID);
802 final String mimeType = entryValues.getAsString(Data.MIMETYPE);
803 if (mimeType == null) continue;
Evan Millar66388be2009-05-28 15:41:07 -0700804
Jeff Sharkey624ddc32009-10-01 21:32:19 -0700805 final DataKind kind = sources.getKindOrFallback(accountType, mimeType, this,
Evan Millar11d628c2009-09-02 08:55:01 -0700806 ContactsSource.LEVEL_MIMETYPES);
Jeff Sharkeyab066932009-09-21 09:55:30 -0700807 if (kind == null) continue;
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800808
Jeff Sharkey624ddc32009-10-01 21:32:19 -0700809 final ViewEntry entry = ViewEntry.fromValues(context, mimeType, kind,
810 rawContactId, dataId, entryValues);
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800811
Jeff Sharkey624ddc32009-10-01 21:32:19 -0700812 final boolean hasData = !TextUtils.isEmpty(entry.data);
Evan Millarff04a272009-09-26 15:23:37 -0700813 final boolean isSuperPrimary = entryValues.getAsInteger(
814 Data.IS_SUPER_PRIMARY) != 0;
Evan Millar11d628c2009-09-02 08:55:01 -0700815
Jeff Sharkey624ddc32009-10-01 21:32:19 -0700816 if (Phone.CONTENT_ITEM_TYPE.equals(mimeType) && hasData) {
Evan Millarff04a272009-09-26 15:23:37 -0700817 // Build phone entries
818 mNumPhoneNumbers++;
Evan Millar11d628c2009-09-02 08:55:01 -0700819
Evan Millarff04a272009-09-26 15:23:37 -0700820 entry.intent = new Intent(Intent.ACTION_CALL_PRIVILEGED,
Jeff Sharkey624ddc32009-10-01 21:32:19 -0700821 Uri.fromParts(Constants.SCHEME_TEL, entry.data, null));
Evan Millarff04a272009-09-26 15:23:37 -0700822 entry.secondaryIntent = new Intent(Intent.ACTION_SENDTO,
Jeff Sharkey624ddc32009-10-01 21:32:19 -0700823 Uri.fromParts(Constants.SCHEME_SMSTO, entry.data, null));
Evan Millar49714ee2009-09-02 16:42:47 -0700824
Evan Millarff04a272009-09-26 15:23:37 -0700825 entry.isPrimary = isSuperPrimary;
826 mPhoneEntries.add(entry);
827
828 if (entry.type == CommonDataKinds.Phone.TYPE_MOBILE
829 || mShowSmsLinksForAllPhones) {
830 // Add an SMS entry
831 if (kind.iconAltRes > 0) {
832 entry.secondaryActionIcon = kind.iconAltRes;
Evan Millar49714ee2009-09-02 16:42:47 -0700833 }
Evan Millarff04a272009-09-26 15:23:37 -0700834 }
Jeff Sharkey624ddc32009-10-01 21:32:19 -0700835 } else if (Email.CONTENT_ITEM_TYPE.equals(mimeType) && hasData) {
Evan Millarff04a272009-09-26 15:23:37 -0700836 // Build email entries
837 entry.intent = new Intent(Intent.ACTION_SENDTO,
Jeff Sharkey624ddc32009-10-01 21:32:19 -0700838 Uri.fromParts(Constants.SCHEME_MAILTO, entry.data, null));
Evan Millarff04a272009-09-26 15:23:37 -0700839 entry.isPrimary = isSuperPrimary;
840 mEmailEntries.add(entry);
Jeff Sharkey624ddc32009-10-01 21:32:19 -0700841
842 // When Email rows have status, create additional Im row
843 final DataStatus status = mStatuses.get(entry.id);
844 if (status != null) {
845 final String imMime = Im.CONTENT_ITEM_TYPE;
846 final DataKind imKind = sources.getKindOrFallback(accountType,
847 imMime, this, ContactsSource.LEVEL_MIMETYPES);
848 final ViewEntry imEntry = ViewEntry.fromValues(context,
849 imMime, imKind, rawContactId, dataId, entryValues);
850 imEntry.intent = ContactsUtils.buildImIntent(entryValues);
851 imEntry.applyStatus(status, false);
852 mImEntries.add(imEntry);
853 }
854 } else if (StructuredPostal.CONTENT_ITEM_TYPE.equals(mimeType) && hasData) {
Evan Millarff04a272009-09-26 15:23:37 -0700855 // Build postal entries
856 entry.maxLines = 4;
Jeff Sharkey624ddc32009-10-01 21:32:19 -0700857 entry.intent = new Intent(Intent.ACTION_VIEW, entry.uri);
Evan Millarff04a272009-09-26 15:23:37 -0700858 mPostalEntries.add(entry);
Jeff Sharkey624ddc32009-10-01 21:32:19 -0700859 } else if (Im.CONTENT_ITEM_TYPE.equals(mimeType) && hasData) {
860 // Build IM entries
861 entry.intent = ContactsUtils.buildImIntent(entryValues);
Evan Millarff04a272009-09-26 15:23:37 -0700862 if (TextUtils.isEmpty(entry.label)) {
863 entry.label = getString(R.string.chat).toLowerCase();
864 }
Evan Millar11d628c2009-09-02 08:55:01 -0700865
Jeff Sharkey624ddc32009-10-01 21:32:19 -0700866 // Apply presence and status details when available
867 final DataStatus status = mStatuses.get(entry.id);
868 if (status != null) {
869 entry.applyStatus(status, false);
Evan Millarff04a272009-09-26 15:23:37 -0700870 }
Evan Millarff04a272009-09-26 15:23:37 -0700871 mImEntries.add(entry);
Jeff Sharkey624ddc32009-10-01 21:32:19 -0700872 } else if ((Organization.CONTENT_ITEM_TYPE.equals(mimeType)
873 || Nickname.CONTENT_ITEM_TYPE.equals(mimeType)) && hasData) {
Evan Millarff04a272009-09-26 15:23:37 -0700874 // Build organization and note entries
875 entry.uri = null;
Evan Millar11d628c2009-09-02 08:55:01 -0700876 mOrganizationEntries.add(entry);
Jeff Sharkey624ddc32009-10-01 21:32:19 -0700877 } else if (Note.CONTENT_ITEM_TYPE.equals(mimeType) && hasData) {
Evan Millar11d628c2009-09-02 08:55:01 -0700878 // Build note entries
Evan Millar11d628c2009-09-02 08:55:01 -0700879 entry.uri = null;
Evan Millar11d628c2009-09-02 08:55:01 -0700880 entry.maxLines = 10;
881 mOtherEntries.add(entry);
Jeff Sharkeyab066932009-09-21 09:55:30 -0700882 } else {
Jeff Sharkey624ddc32009-10-01 21:32:19 -0700883 // Handle showing custom rows
Evan Millarff04a272009-09-26 15:23:37 -0700884 entry.intent = new Intent(Intent.ACTION_VIEW, entry.uri);
Jeff Sharkey624ddc32009-10-01 21:32:19 -0700885
886 // Use social summary when requested by external source
887 final DataStatus status = mStatuses.get(entry.id);
888 final boolean hasSocial = kind.actionBodySocial && status != null;
889 if (hasSocial) {
890 entry.applyStatus(status, true);
891 }
892
893 if (hasSocial || hasData) {
894 mOtherEntries.add(entry);
895 }
Evan Millar11d628c2009-09-02 08:55:01 -0700896 }
Evan Millar2e1b8af2009-09-24 09:39:21 -0700897 }
Evan Millar66388be2009-05-28 15:41:07 -0700898 }
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800899 }
900 }
901
Jeff Sharkey624ddc32009-10-01 21:32:19 -0700902 static String buildActionString(DataKind kind, ContentValues values, boolean lowerCase,
903 Context context) {
Evan Millar11d628c2009-09-02 08:55:01 -0700904 if (kind.actionHeader == null) {
905 return null;
Jeff Hamilton8350e5b2009-03-24 21:01:34 -0700906 }
Jeff Sharkey624ddc32009-10-01 21:32:19 -0700907 CharSequence actionHeader = kind.actionHeader.inflateUsing(context, values);
Evan Millar11d628c2009-09-02 08:55:01 -0700908 if (actionHeader == null) {
909 return null;
910 }
911 return lowerCase ? actionHeader.toString().toLowerCase() : actionHeader.toString();
912 }
Jeff Hamilton8350e5b2009-03-24 21:01:34 -0700913
Jeff Sharkey624ddc32009-10-01 21:32:19 -0700914 static String buildDataString(DataKind kind, ContentValues values, Context context) {
Evan Millar11d628c2009-09-02 08:55:01 -0700915 if (kind.actionBody == null) {
916 return null;
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800917 }
Jeff Sharkey624ddc32009-10-01 21:32:19 -0700918 CharSequence actionBody = kind.actionBody.inflateUsing(context, values);
Evan Millar11d628c2009-09-02 08:55:01 -0700919 return actionBody == null ? null : actionBody.toString();
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800920 }
Evan Millar5c22c3b2009-05-29 11:37:54 -0700921
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800922 /**
923 * A basic structure with the data for a contact entry in the list.
924 */
Jeff Sharkey624ddc32009-10-01 21:32:19 -0700925 static class ViewEntry extends ContactEntryAdapter.Entry implements Collapsible<ViewEntry> {
926 public Context context = null;
Jeff Sharkeyab066932009-09-21 09:55:30 -0700927 public String resPackageName = null;
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800928 public int actionIcon = -1;
Evan Millar15e514d2009-08-04 10:14:57 -0700929 public boolean isPrimary = false;
Evan Millar15e514d2009-08-04 10:14:57 -0700930 public int secondaryActionIcon = -1;
931 public Intent intent;
932 public Intent secondaryIntent = null;
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800933 public int maxLabelLines = 1;
Evan Millar54a5c9f2009-06-23 17:41:09 -0700934 public ArrayList<Long> ids = new ArrayList<Long>();
935 public int collapseCount = 0;
936
Jeff Sharkey624ddc32009-10-01 21:32:19 -0700937 public int presence = -1;
938 public int presenceIcon = -1;
939
940 public CharSequence footerLine = null;
941
942 private ViewEntry() {
943 }
944
945 /**
946 * Build new {@link ViewEntry} and populate from the given values.
947 */
948 public static ViewEntry fromValues(Context context, String mimeType, DataKind kind,
949 long rawContactId, long dataId, ContentValues values) {
950 final ViewEntry entry = new ViewEntry();
951 entry.context = context;
952 entry.contactId = rawContactId;
953 entry.id = dataId;
954 entry.uri = ContentUris.withAppendedId(Data.CONTENT_URI, entry.id);
955 entry.mimetype = mimeType;
956 entry.label = buildActionString(kind, values, false, context);
957 entry.data = buildDataString(kind, values, context);
958
959 if (kind.typeColumn != null && values.containsKey(kind.typeColumn)) {
960 entry.type = values.getAsInteger(kind.typeColumn);
961 }
962 if (kind.iconRes > 0) {
963 entry.resPackageName = kind.resPackageName;
964 entry.actionIcon = kind.iconRes;
965 }
966
967 return entry;
968 }
969
970 /**
971 * Apply given {@link DataStatus} values over this {@link ViewEntry}
972 *
973 * @param fillData When true, the given status replaces {@link #data}
974 * and {@link #footerLine}. Otherwise only {@link #presence}
975 * is updated.
976 */
977 public ViewEntry applyStatus(DataStatus status, boolean fillData) {
978 presence = status.getPresence();
979 presenceIcon = (presence == -1) ? -1 :
980 StatusUpdates.getPresenceIconResourceId(this.presence);
981
982 if (fillData && status.isValid()) {
983 this.data = status.getStatus().toString();
984 this.footerLine = status.getTimestampLabel(context);
985 }
986
987 return this;
988 }
989
Evan Millar54a5c9f2009-06-23 17:41:09 -0700990 public boolean collapseWith(ViewEntry entry) {
991 // assert equal collapse keys
Evan Millaradb0f8c2009-09-28 17:20:50 -0700992 if (!shouldCollapseWith(entry)) {
Evan Millar54a5c9f2009-06-23 17:41:09 -0700993 return false;
994 }
995
996 // Choose the label associated with the highest type precedence.
997 if (TypePrecedence.getTypePrecedence(mimetype, type)
998 > TypePrecedence.getTypePrecedence(entry.mimetype, entry.type)) {
999 type = entry.type;
1000 label = entry.label;
1001 }
1002
1003 // Choose the max of the maxLines and maxLabelLines values.
1004 maxLines = Math.max(maxLines, entry.maxLines);
1005 maxLabelLines = Math.max(maxLabelLines, entry.maxLabelLines);
1006
1007 // Choose the presence with the highest precedence.
Jeff Sharkey624ddc32009-10-01 21:32:19 -07001008 if (StatusUpdates.getPresencePrecedence(presence)
1009 < StatusUpdates.getPresencePrecedence(entry.presence)) {
1010 presence = entry.presence;
Evan Millar54a5c9f2009-06-23 17:41:09 -07001011 }
1012
1013 // If any of the collapsed entries are primary make the whole thing primary.
Evan Millar15e514d2009-08-04 10:14:57 -07001014 isPrimary = entry.isPrimary ? true : isPrimary;
Evan Millar54a5c9f2009-06-23 17:41:09 -07001015
1016 // uri, and contactdId, shouldn't make a difference. Just keep the original.
1017
1018 // Keep track of all the ids that have been collapsed with this one.
1019 ids.add(entry.id);
1020 collapseCount++;
1021 return true;
1022 }
1023
Evan Millaradb0f8c2009-09-28 17:20:50 -07001024 public boolean shouldCollapseWith(ViewEntry entry) {
1025 if (entry == null) {
1026 return false;
1027 }
1028
Makoto Onukic710b0e2009-10-13 15:43:24 -07001029 if (!ContactsUtils.areDataEqual(context, mimetype, data, entry.mimetype, entry.data)) {
1030 return false;
Evan Millaradb0f8c2009-09-28 17:20:50 -07001031 }
1032
Makoto Onukic710b0e2009-10-13 15:43:24 -07001033 if (!TextUtils.equals(mimetype, entry.mimetype)
1034 || !ContactsUtils.areIntentActionEqual(intent, entry.intent)
1035 || !ContactsUtils.areIntentActionEqual(secondaryIntent, entry.secondaryIntent)
Evan Millaradb0f8c2009-09-28 17:20:50 -07001036 || actionIcon != entry.actionIcon) {
1037 return false;
1038 }
1039
1040 return true;
1041 }
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08001042 }
1043
Evan Millar15e514d2009-08-04 10:14:57 -07001044 /** Cache of the children views of a row */
1045 static class ViewCache {
1046 public TextView label;
1047 public TextView data;
Jeff Sharkey624ddc32009-10-01 21:32:19 -07001048 public TextView footer;
Evan Millar15e514d2009-08-04 10:14:57 -07001049 public ImageView actionIcon;
1050 public ImageView presenceIcon;
1051 public ImageView primaryIcon;
1052 public ImageView secondaryActionButton;
1053 public View secondaryActionDivider;
Evan Millar5c22c3b2009-05-29 11:37:54 -07001054
Evan Millar15e514d2009-08-04 10:14:57 -07001055 // Need to keep track of this too
1056 ViewEntry entry;
1057 }
1058
1059 private final class ViewAdapter extends ContactEntryAdapter<ViewEntry>
1060 implements View.OnClickListener {
1061
Evan Millar5c22c3b2009-05-29 11:37:54 -07001062
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08001063 ViewAdapter(Context context, ArrayList<ArrayList<ViewEntry>> sections) {
1064 super(context, sections, SHOW_SEPARATORS);
1065 }
1066
Evan Millar15e514d2009-08-04 10:14:57 -07001067 public void onClick(View v) {
1068 Intent intent = (Intent) v.getTag();
1069 startActivity(intent);
1070 }
1071
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08001072 @Override
1073 public View getView(int position, View convertView, ViewGroup parent) {
Evan Millar5c22c3b2009-05-29 11:37:54 -07001074 ViewEntry entry = getEntry(mSections, position, false);
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08001075 View v;
1076
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08001077 ViewCache views;
1078
1079 // Check to see if we can reuse convertView
1080 if (convertView != null) {
1081 v = convertView;
1082 views = (ViewCache) v.getTag();
1083 } else {
1084 // Create a new view if needed
1085 v = mInflater.inflate(R.layout.list_item_text_icons, parent, false);
1086
1087 // Cache the children
1088 views = new ViewCache();
1089 views.label = (TextView) v.findViewById(android.R.id.text1);
1090 views.data = (TextView) v.findViewById(android.R.id.text2);
Jeff Sharkey624ddc32009-10-01 21:32:19 -07001091 views.footer = (TextView) v.findViewById(R.id.footer);
Evan Millar15e514d2009-08-04 10:14:57 -07001092 views.actionIcon = (ImageView) v.findViewById(R.id.action_icon);
1093 views.primaryIcon = (ImageView) v.findViewById(R.id.primary_icon);
1094 views.presenceIcon = (ImageView) v.findViewById(R.id.presence_icon);
1095 views.secondaryActionButton = (ImageView) v.findViewById(
1096 R.id.secondary_action_button);
1097 views.secondaryActionButton.setOnClickListener(this);
Evan Millar15e514d2009-08-04 10:14:57 -07001098 views.secondaryActionDivider = v.findViewById(R.id.divider);
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08001099 v.setTag(views);
1100 }
1101
1102 // Update the entry in the view cache
1103 views.entry = entry;
1104
1105 // Bind the data to the view
1106 bindView(v, entry);
1107 return v;
1108 }
1109
1110 @Override
1111 protected View newView(int position, ViewGroup parent) {
1112 // getView() handles this
1113 throw new UnsupportedOperationException();
1114 }
1115
1116 @Override
1117 protected void bindView(View view, ViewEntry entry) {
1118 final Resources resources = mContext.getResources();
1119 ViewCache views = (ViewCache) view.getTag();
1120
1121 // Set the label
1122 TextView label = views.label;
1123 setMaxLines(label, entry.maxLabelLines);
1124 label.setText(entry.label);
1125
1126 // Set the data
1127 TextView data = views.data;
1128 if (data != null) {
Evan Millar54a5c9f2009-06-23 17:41:09 -07001129 if (entry.mimetype.equals(Phone.CONTENT_ITEM_TYPE)
Jeff Sharkey49d17b32009-09-07 02:14:21 -07001130 || entry.mimetype.equals(Constants.MIME_SMS_ADDRESS)) {
Evan Millar54a5c9f2009-06-23 17:41:09 -07001131 data.setText(PhoneNumberUtils.formatNumber(entry.data));
1132 } else {
1133 data.setText(entry.data);
1134 }
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08001135 setMaxLines(data, entry.maxLines);
1136 }
1137
Jeff Sharkey624ddc32009-10-01 21:32:19 -07001138 // Set the footer
1139 if (!TextUtils.isEmpty(entry.footerLine)) {
1140 views.footer.setText(entry.footerLine);
1141 views.footer.setVisibility(View.VISIBLE);
1142 } else {
1143 views.footer.setVisibility(View.GONE);
1144 }
1145
Evan Millar15e514d2009-08-04 10:14:57 -07001146 // Set the primary icon
1147 views.primaryIcon.setVisibility(entry.isPrimary ? View.VISIBLE : View.GONE);
1148
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08001149 // Set the action icon
1150 ImageView action = views.actionIcon;
1151 if (entry.actionIcon != -1) {
Jeff Sharkeyab066932009-09-21 09:55:30 -07001152 Drawable actionIcon;
1153 if (entry.resPackageName != null) {
1154 // Load external resources through PackageManager
1155 actionIcon = mContext.getPackageManager().getDrawable(entry.resPackageName,
1156 entry.actionIcon, null);
1157 } else {
1158 actionIcon = resources.getDrawable(entry.actionIcon);
1159 }
1160 action.setImageDrawable(actionIcon);
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08001161 action.setVisibility(View.VISIBLE);
1162 } else {
1163 // Things should still line up as if there was an icon, so make it invisible
1164 action.setVisibility(View.INVISIBLE);
1165 }
1166
1167 // Set the presence icon
1168 Drawable presenceIcon = null;
Evan Millar15e514d2009-08-04 10:14:57 -07001169 if (entry.presenceIcon != -1) {
1170 presenceIcon = resources.getDrawable(entry.presenceIcon);
Jeff Sharkey624ddc32009-10-01 21:32:19 -07001171 } else if (entry.presence != -1) {
Evan Millar54a5c9f2009-06-23 17:41:09 -07001172 presenceIcon = resources.getDrawable(
Jeff Sharkey624ddc32009-10-01 21:32:19 -07001173 StatusUpdates.getPresenceIconResourceId(entry.presence));
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08001174 }
Evan Millar15e514d2009-08-04 10:14:57 -07001175 ImageView presenceIconView = views.presenceIcon;
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08001176 if (presenceIcon != null) {
Evan Millar15e514d2009-08-04 10:14:57 -07001177 presenceIconView.setImageDrawable(presenceIcon);
1178 presenceIconView.setVisibility(View.VISIBLE);
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08001179 } else {
Evan Millar15e514d2009-08-04 10:14:57 -07001180 presenceIconView.setVisibility(View.GONE);
1181 }
1182
1183 // Set the secondary action button
1184 ImageView secondaryActionView = views.secondaryActionButton;
1185 Drawable secondaryActionIcon = null;
1186 if (entry.secondaryActionIcon != -1) {
1187 secondaryActionIcon = resources.getDrawable(entry.secondaryActionIcon);
1188 }
1189 if (entry.secondaryIntent != null && secondaryActionIcon != null) {
1190 secondaryActionView.setImageDrawable(secondaryActionIcon);
1191 secondaryActionView.setTag(entry.secondaryIntent);
1192 secondaryActionView.setVisibility(View.VISIBLE);
1193 views.secondaryActionDivider.setVisibility(View.VISIBLE);
1194 } else {
1195 secondaryActionView.setVisibility(View.GONE);
1196 views.secondaryActionDivider.setVisibility(View.GONE);
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08001197 }
1198 }
1199
1200 private void setMaxLines(TextView textView, int maxLines) {
1201 if (maxLines == 1) {
1202 textView.setSingleLine(true);
1203 textView.setEllipsize(TextUtils.TruncateAt.END);
1204 } else {
1205 textView.setSingleLine(false);
1206 textView.setMaxLines(maxLines);
1207 textView.setEllipsize(null);
1208 }
1209 }
1210 }
Jeff Sharkey624ddc32009-10-01 21:32:19 -07001211
1212 private interface StatusQuery {
1213 final String[] PROJECTION = new String[] {
1214 Data._ID,
1215 Data.STATUS,
1216 Data.STATUS_RES_PACKAGE,
1217 Data.STATUS_ICON,
1218 Data.STATUS_LABEL,
1219 Data.STATUS_TIMESTAMP,
1220 Data.PRESENCE,
1221 };
1222
1223 final int _ID = 0;
1224 }
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08001225}