blob: 2fd22b19aedba068f300596a06bd657154c79dea [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
Dmitri Plotnikove1cd6792009-07-27 20:28:17 -070019import static com.android.contacts.ContactEntryAdapter.CONTACT_PHOTO_ID;
20import static com.android.contacts.ContactEntryAdapter.CONTACT_PROJECTION;
21import static com.android.contacts.ContactEntryAdapter.CONTACT_STARRED_COLUMN;
Evan Millar66388be2009-05-28 15:41:07 -070022import static com.android.contacts.ContactEntryAdapter.DATA_1_COLUMN;
23import static com.android.contacts.ContactEntryAdapter.DATA_2_COLUMN;
24import static com.android.contacts.ContactEntryAdapter.DATA_3_COLUMN;
25import static com.android.contacts.ContactEntryAdapter.DATA_4_COLUMN;
26import static com.android.contacts.ContactEntryAdapter.DATA_5_COLUMN;
Evan Millar66388be2009-05-28 15:41:07 -070027import static com.android.contacts.ContactEntryAdapter.DATA_9_COLUMN;
Dmitri Plotnikovb4491ee2009-06-15 09:31:02 -070028import static com.android.contacts.ContactEntryAdapter.DATA_CONTACT_ID_COLUMN;
29import static com.android.contacts.ContactEntryAdapter.DATA_ID_COLUMN;
30import static com.android.contacts.ContactEntryAdapter.DATA_IS_SUPER_PRIMARY_COLUMN;
31import static com.android.contacts.ContactEntryAdapter.DATA_MIMETYPE_COLUMN;
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -080032
Evan Millar54a5c9f2009-06-23 17:41:09 -070033import com.android.contacts.Collapser.Collapsible;
Dmitri Plotnikovb4491ee2009-06-15 09:31:02 -070034import com.android.contacts.SplitAggregateView.OnContactSelectedListener;
Jeff Sharkey802b2052009-08-04 14:21:06 -070035import com.android.contacts.ui.FastTrackWindow;
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -080036import com.android.internal.telephony.ITelephony;
37
38import android.app.AlertDialog;
39import android.app.Dialog;
40import android.app.ListActivity;
41import android.content.ActivityNotFoundException;
42import android.content.ContentResolver;
43import android.content.ContentUris;
44import android.content.ContentValues;
45import android.content.Context;
46import android.content.DialogInterface;
Evan Millar5f4af702009-08-11 11:12:00 -070047import android.content.Entity;
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -080048import android.content.Intent;
Dmitri Plotnikovb4491ee2009-06-15 09:31:02 -070049import android.content.DialogInterface.OnClickListener;
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -080050import android.content.pm.PackageManager;
51import android.content.pm.ResolveInfo;
52import android.content.res.Resources;
53import android.database.ContentObserver;
54import android.database.Cursor;
Evan Millar45e0ed32009-06-01 16:44:38 -070055import android.graphics.Bitmap;
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -080056import android.graphics.drawable.Drawable;
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -080057import android.net.Uri;
58import android.os.Bundle;
59import android.os.Handler;
60import android.os.RemoteException;
61import android.os.ServiceManager;
62import android.os.SystemClock;
Dmitri Plotnikove1cd6792009-07-27 20:28:17 -070063import android.provider.ContactsContract.Contacts;
Dmitri Plotnikovb4491ee2009-06-15 09:31:02 -070064import android.provider.ContactsContract.AggregationExceptions;
65import android.provider.ContactsContract.CommonDataKinds;
Evan Millar66388be2009-05-28 15:41:07 -070066import android.provider.ContactsContract.Data;
Evan Millar54a5c9f2009-06-23 17:41:09 -070067import android.provider.ContactsContract.Presence;
Dmitri Plotnikov39466592009-07-27 11:23:51 -070068import android.provider.ContactsContract.RawContacts;
Evan Millar54a5c9f2009-06-23 17:41:09 -070069import android.provider.ContactsContract.CommonDataKinds.Phone;
70import android.telephony.PhoneNumberUtils;
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -080071import android.text.TextUtils;
72import android.util.Log;
73import android.view.ContextMenu;
Dmitri Plotnikovb4491ee2009-06-15 09:31:02 -070074import android.view.ContextThemeWrapper;
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -080075import android.view.KeyEvent;
Evan Millar7911ff52009-07-21 15:55:18 -070076import android.view.LayoutInflater;
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -080077import android.view.Menu;
78import android.view.MenuItem;
79import android.view.View;
80import android.view.ViewGroup;
81import android.view.ContextMenu.ContextMenuInfo;
82import android.widget.AdapterView;
83import android.widget.CheckBox;
Evan Millar7911ff52009-07-21 15:55:18 -070084import android.widget.FrameLayout;
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -080085import android.widget.ImageView;
86import android.widget.ListView;
87import android.widget.TextView;
88import android.widget.Toast;
89
90import java.util.ArrayList;
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -080091
92/**
93 * Displays the details of a specific contact.
94 */
Evan Millar7911ff52009-07-21 15:55:18 -070095public class ViewContactActivity extends BaseContactCardActivity
96 implements View.OnCreateContextMenuListener, DialogInterface.OnClickListener,
97 AdapterView.OnItemClickListener {
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -080098 private static final String TAG = "ViewContact";
99 private static final String SHOW_BARCODE_INTENT = "com.google.zxing.client.android.ENCODE";
100
Evan Millar8a79cee2009-08-19 17:20:49 -0700101 public static final String RAW_CONTACT_ID_EXTRA = "rawContactIdExtra";
102
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800103 private static final boolean SHOW_SEPARATORS = false;
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800104
105 private static final int DIALOG_CONFIRM_DELETE = 1;
106
Evan Millar8a79cee2009-08-19 17:20:49 -0700107 private static final int REQUEST_JOIN_CONTACT = 1;
108 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_EDIT = 1;
111 public static final int MENU_ITEM_DELETE = 2;
112 public static final int MENU_ITEM_MAKE_DEFAULT = 3;
113 public static final int MENU_ITEM_SHOW_BARCODE = 4;
114 public static final int MENU_ITEM_SPLIT_AGGREGATE = 5;
115 public static final int MENU_ITEM_JOIN_AGGREGATE = 6;
116 public static final int MENU_ITEM_OPTIONS = 7;
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800117
118 private Uri mUri;
119 private ContentResolver mResolver;
120 private ViewAdapter mAdapter;
121 private int mNumPhoneNumbers = 0;
122
Evan Millar8a79cee2009-08-19 17:20:49 -0700123 private static final long ALL_CONTACTS_ID = -100;
Evan Millar7911ff52009-07-21 15:55:18 -0700124
Dmitri Plotnikovb4491ee2009-06-15 09:31:02 -0700125 /**
Dmitri Plotnikove1cd6792009-07-27 20:28:17 -0700126 * A list of distinct contact IDs included in the current contact.
Dmitri Plotnikovb4491ee2009-06-15 09:31:02 -0700127 */
Evan Millar7911ff52009-07-21 15:55:18 -0700128 private ArrayList<Long> mRawContactIds = new ArrayList<Long>();
Dmitri Plotnikovb4491ee2009-06-15 09:31:02 -0700129
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800130 /* package */ ArrayList<ViewEntry> mPhoneEntries = new ArrayList<ViewEntry>();
131 /* package */ ArrayList<ViewEntry> mSmsEntries = new ArrayList<ViewEntry>();
132 /* package */ ArrayList<ViewEntry> mEmailEntries = new ArrayList<ViewEntry>();
133 /* package */ ArrayList<ViewEntry> mPostalEntries = new ArrayList<ViewEntry>();
134 /* package */ ArrayList<ViewEntry> mImEntries = new ArrayList<ViewEntry>();
135 /* package */ ArrayList<ViewEntry> mOrganizationEntries = new ArrayList<ViewEntry>();
The Android Open Source Projectcac191e2009-03-18 22:20:27 -0700136 /* package */ ArrayList<ViewEntry> mGroupEntries = new ArrayList<ViewEntry>();
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800137 /* package */ ArrayList<ViewEntry> mOtherEntries = new ArrayList<ViewEntry>();
138 /* package */ ArrayList<ArrayList<ViewEntry>> mSections = new ArrayList<ArrayList<ViewEntry>>();
139
140 private Cursor mCursor;
141 private boolean mObserverRegistered;
Evan Millar5c22c3b2009-05-29 11:37:54 -0700142
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800143 private ContentObserver mObserver = new ContentObserver(new Handler()) {
144 @Override
145 public boolean deliverSelfNotifications() {
146 return true;
147 }
148
149 @Override
150 public void onChange(boolean selfChange) {
151 if (mCursor != null && !mCursor.isClosed()){
152 dataChanged();
153 }
154 }
155 };
156
157 public void onClick(DialogInterface dialog, int which) {
158 if (mCursor != null) {
159 if (mObserverRegistered) {
160 mCursor.unregisterContentObserver(mObserver);
161 mObserverRegistered = false;
162 }
163 mCursor.close();
164 mCursor = null;
165 }
166 getContentResolver().delete(mUri, null, null);
167 finish();
168 }
169
Evan Millar7911ff52009-07-21 15:55:18 -0700170 private FrameLayout mTabContentLayout;
171 private ListView mListView;
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800172 private boolean mShowSmsLinksForAllPhones;
173
174 @Override
175 protected void onCreate(Bundle icicle) {
176 super.onCreate(icicle);
177
Evan Millar7911ff52009-07-21 15:55:18 -0700178 mListView = new ListView(this);
179 mListView.setOnCreateContextMenuListener(this);
180 mListView.setScrollBarStyle(ListView.SCROLLBARS_OUTSIDE_OVERLAY);
181 mListView.setOnItemClickListener(this);
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800182
Evan Millar7911ff52009-07-21 15:55:18 -0700183 mTabContentLayout = (FrameLayout) findViewById(android.R.id.tabcontent);
184 mTabContentLayout.addView(mListView);
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800185
Evan Millar45e0ed32009-06-01 16:44:38 -0700186 mUri = getIntent().getData();
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800187 mResolver = getContentResolver();
188
189 // Build the list of sections. The order they're added to mSections dictates the
190 // order they are displayed in the list.
191 mSections.add(mPhoneEntries);
192 mSections.add(mSmsEntries);
193 mSections.add(mEmailEntries);
194 mSections.add(mImEntries);
195 mSections.add(mPostalEntries);
196 mSections.add(mOrganizationEntries);
The Android Open Source Projectcac191e2009-03-18 22:20:27 -0700197 mSections.add(mGroupEntries);
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800198 mSections.add(mOtherEntries);
199
200 //TODO Read this value from a preference
201 mShowSmsLinksForAllPhones = true;
202
Evan Millar8a79cee2009-08-19 17:20:49 -0700203 //Select the all tab to start.
204 mSelectedRawContactId = ALL_CONTACTS_ID;
205
206 mCursor = mResolver.query(Uri.withAppendedPath(mUri, "data"),
Dmitri Plotnikove1cd6792009-07-27 20:28:17 -0700207 CONTACT_PROJECTION, null, null, null);
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800208 }
209
210 @Override
211 protected void onResume() {
212 super.onResume();
213 mObserverRegistered = true;
214 mCursor.registerContentObserver(mObserver);
215 dataChanged();
216 }
217
218 @Override
219 protected void onPause() {
220 super.onPause();
221 if (mCursor != null) {
222 if (mObserverRegistered) {
223 mObserverRegistered = false;
224 mCursor.unregisterContentObserver(mObserver);
225 }
226 mCursor.deactivate();
227 }
228 }
229
230 @Override
231 protected void onDestroy() {
232 super.onDestroy();
233
234 if (mCursor != null) {
235 if (mObserverRegistered) {
236 mCursor.unregisterContentObserver(mObserver);
237 mObserverRegistered = false;
238 }
239 mCursor.close();
240 }
241 }
242
243 @Override
244 protected Dialog onCreateDialog(int id) {
245 switch (id) {
246 case DIALOG_CONFIRM_DELETE:
247 return new AlertDialog.Builder(this)
248 .setTitle(R.string.deleteConfirmation_title)
249 .setIcon(android.R.drawable.ic_dialog_alert)
250 .setMessage(R.string.deleteConfirmation)
251 .setNegativeButton(android.R.string.cancel, null)
252 .setPositiveButton(android.R.string.ok, this)
253 .setCancelable(false)
254 .create();
255 }
256 return null;
257 }
258
Evan Millar7911ff52009-07-21 15:55:18 -0700259 @Override
Evan Millar5f4af702009-08-11 11:12:00 -0700260 protected void bindTabs(ArrayList<Entity> entities) {
261 if (entities.size() > 1) {
Evan Millar7911ff52009-07-21 15:55:18 -0700262 addAllTab();
263 }
Evan Millar5f4af702009-08-11 11:12:00 -0700264 super.bindTabs(entities);
Evan Millar7911ff52009-07-21 15:55:18 -0700265 }
266
267 private void addAllTab() {
268 View allTabIndicator = mInflater.inflate(R.layout.all_tab_indicator, mTabWidget, false);
Evan Millar74660912009-08-19 17:36:33 -0700269 allTabIndicator.getBackground().setDither(true);
Evan Millar7911ff52009-07-21 15:55:18 -0700270 addTab(ALL_CONTACTS_ID, allTabIndicator);
271 }
272
273 public void onTabSelectionChanged(int tabIndex, boolean clicked) {
274 long rawContactId = getTabRawContactId(tabIndex);
Evan Millar8a79cee2009-08-19 17:20:49 -0700275 mSelectedRawContactId = rawContactId;
Evan Millar7911ff52009-07-21 15:55:18 -0700276 dataChanged();
277 }
278
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800279 private void dataChanged() {
280 mCursor.requery();
281 if (mCursor.moveToFirst()) {
Evan Millar2c1cc832009-07-13 11:08:06 -0700282
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800283 // Build up the contact entries
284 buildEntries(mCursor);
Evan Millar54a5c9f2009-06-23 17:41:09 -0700285
286 // Collapse similar data items in select sections.
287 Collapser.collapseList(mPhoneEntries);
288 Collapser.collapseList(mSmsEntries);
289 Collapser.collapseList(mEmailEntries);
290 Collapser.collapseList(mPostalEntries);
291
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800292 if (mAdapter == null) {
293 mAdapter = new ViewAdapter(this, mSections);
Evan Millar7911ff52009-07-21 15:55:18 -0700294 mListView.setAdapter(mAdapter);
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800295 } else {
296 mAdapter.setSections(mSections, SHOW_SEPARATORS);
297 }
298 } else {
299 Toast.makeText(this, R.string.invalidContactMessage, Toast.LENGTH_SHORT).show();
300 Log.e(TAG, "invalid contact uri: " + mUri);
301 finish();
302 }
303 }
304
305 @Override
306 public boolean onCreateOptionsMenu(Menu menu) {
Evan Millar8a79cee2009-08-19 17:20:49 -0700307 menu.add(0, MENU_ITEM_EDIT, 0, R.string.menu_editContact)
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800308 .setIcon(android.R.drawable.ic_menu_edit)
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800309 .setAlphabeticShortcut('e');
310 menu.add(0, MENU_ITEM_DELETE, 0, R.string.menu_deleteContact)
311 .setIcon(android.R.drawable.ic_menu_delete);
Dmitri Plotnikovb4491ee2009-06-15 09:31:02 -0700312 menu.add(0, MENU_ITEM_SPLIT_AGGREGATE, 0, R.string.menu_splitAggregate)
313 .setIcon(android.R.drawable.ic_menu_share);
Dmitri Plotnikov49f705f2009-06-17 18:31:56 -0700314 menu.add(0, MENU_ITEM_JOIN_AGGREGATE, 0, R.string.menu_joinAggregate)
315 .setIcon(android.R.drawable.ic_menu_add);
Dmitri Plotnikovef038722009-06-24 18:51:47 -0700316 menu.add(0, MENU_ITEM_OPTIONS, 0, R.string.menu_contactOptions)
317 .setIcon(R.drawable.ic_menu_mark);
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800318 return true;
319 }
320
321 @Override
322 public boolean onPrepareOptionsMenu(Menu menu) {
323 super.onPrepareOptionsMenu(menu);
324 // Perform this check each time the menu is about to be shown, because the Barcode Scanner
325 // could be installed or uninstalled at any time.
326 if (isBarcodeScannerInstalled()) {
327 if (menu.findItem(MENU_ITEM_SHOW_BARCODE) == null) {
328 menu.add(0, MENU_ITEM_SHOW_BARCODE, 0, R.string.menu_showBarcode)
329 .setIcon(R.drawable.ic_menu_show_barcode);
330 }
331 } else {
332 menu.removeItem(MENU_ITEM_SHOW_BARCODE);
333 }
Dmitri Plotnikovb4491ee2009-06-15 09:31:02 -0700334
Evan Millar7911ff52009-07-21 15:55:18 -0700335 boolean isAggregate = mRawContactIds.size() > 1;
Dmitri Plotnikovb4491ee2009-06-15 09:31:02 -0700336 menu.findItem(MENU_ITEM_SPLIT_AGGREGATE).setEnabled(isAggregate);
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800337 return true;
338 }
339
340 private boolean isBarcodeScannerInstalled() {
341 final Intent intent = new Intent(SHOW_BARCODE_INTENT);
342 ResolveInfo ri = getPackageManager().resolveActivity(intent,
343 PackageManager.MATCH_DEFAULT_ONLY);
344 return ri != null;
345 }
346
347 @Override
348 public void onCreateContextMenu(ContextMenu menu, View view, ContextMenuInfo menuInfo) {
349 AdapterView.AdapterContextMenuInfo info;
350 try {
351 info = (AdapterView.AdapterContextMenuInfo) menuInfo;
352 } catch (ClassCastException e) {
353 Log.e(TAG, "bad menuInfo", e);
354 return;
355 }
356
357 // This can be null sometimes, don't crash...
358 if (info == null) {
359 Log.e(TAG, "bad menuInfo");
360 return;
361 }
Evan Millar5c22c3b2009-05-29 11:37:54 -0700362
Evan Millar45e0ed32009-06-01 16:44:38 -0700363 ViewEntry entry = ContactEntryAdapter.getEntry(mSections, info.position, SHOW_SEPARATORS);
364 if (entry.mimetype.equals(CommonDataKinds.Phone.CONTENT_ITEM_TYPE)) {
365 menu.add(0, 0, 0, R.string.menu_call).setIntent(entry.intent);
Evan Millar15e514d2009-08-04 10:14:57 -0700366 menu.add(0, 0, 0, R.string.menu_sendSMS).setIntent(entry.secondaryIntent);
367 if (!entry.isPrimary) {
Evan Millar45e0ed32009-06-01 16:44:38 -0700368 menu.add(0, MENU_ITEM_MAKE_DEFAULT, 0, R.string.menu_makeDefaultNumber);
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800369 }
Evan Millar45e0ed32009-06-01 16:44:38 -0700370 } else if (entry.mimetype.equals(CommonDataKinds.Email.CONTENT_ITEM_TYPE)) {
371 menu.add(0, 0, 0, R.string.menu_sendEmail).setIntent(entry.intent);
Evan Millar15e514d2009-08-04 10:14:57 -0700372 if (!entry.isPrimary) {
Evan Millar45e0ed32009-06-01 16:44:38 -0700373 menu.add(0, MENU_ITEM_MAKE_DEFAULT, 0, R.string.menu_makeDefaultEmail);
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800374 }
Jeff Sharkeyc6ad3ab2009-07-21 19:30:15 -0700375 } else if (entry.mimetype.equals(CommonDataKinds.StructuredPostal.CONTENT_ITEM_TYPE)) {
Evan Millar45e0ed32009-06-01 16:44:38 -0700376 menu.add(0, 0, 0, R.string.menu_viewAddress).setIntent(entry.intent);
377 }
378 // TODO(emillar): add back with group support.
379 /* else if (entry.mimetype.equals()) {
380 menu.add(0, 0, 0, R.string.menu_viewGroup).setIntent(entry.intent);
381 } */
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800382 }
383
384 @Override
385 public boolean onOptionsItemSelected(MenuItem item) {
386 switch (item.getItemId()) {
Evan Millar8a79cee2009-08-19 17:20:49 -0700387 case MENU_ITEM_EDIT: {
388 long rawContactIdToEdit = mSelectedRawContactId;
389 if (rawContactIdToEdit == ALL_CONTACTS_ID) {
390 // If the "all" tab is selected, edit the next tab.
391 rawContactIdToEdit = getTabRawContactId(mTabWidget.getCurrentTab() + 1);
392 }
393 Uri rawContactUri = ContentUris.withAppendedId(RawContacts.CONTENT_URI,
394 rawContactIdToEdit);
395 startActivityForResult(new Intent(Intent.ACTION_EDIT, rawContactUri),
396 REQUEST_EDIT_CONTACT);
397 break;
398 }
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800399 case MENU_ITEM_DELETE: {
400 // Get confirmation
401 showDialog(DIALOG_CONFIRM_DELETE);
402 return true;
403 }
Evan Millar5c22c3b2009-05-29 11:37:54 -0700404
Dmitri Plotnikovb4491ee2009-06-15 09:31:02 -0700405 case MENU_ITEM_SPLIT_AGGREGATE: {
406 showSplitAggregateDialog();
407 return true;
408 }
409
Dmitri Plotnikov49f705f2009-06-17 18:31:56 -0700410 case MENU_ITEM_JOIN_AGGREGATE: {
411 showJoinAggregateActivity();
412 return true;
413 }
414
Dmitri Plotnikovef038722009-06-24 18:51:47 -0700415 case MENU_ITEM_OPTIONS: {
416 showOptionsActivity();
417 return true;
418 }
419
Evan Millar66388be2009-05-28 15:41:07 -0700420 // TODO(emillar) Bring this back.
421 /*case MENU_ITEM_SHOW_BARCODE:
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800422 if (mCursor.moveToFirst()) {
423 Intent intent = new Intent(SHOW_BARCODE_INTENT);
424 intent.putExtra("ENCODE_TYPE", "CONTACT_TYPE");
425 Bundle bundle = new Bundle();
Evan Millar66388be2009-05-28 15:41:07 -0700426 String name = mCursor.getString(AGGREGATE_DISPLAY_NAME_COLUMN);
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800427 if (!TextUtils.isEmpty(name)) {
Jeffrey Sharkey715b32a2009-03-27 18:56:05 -0700428 // Correctly handle when section headers are hidden
429 int sepAdjust = SHOW_SEPARATORS ? 1 : 0;
Evan Millar5c22c3b2009-05-29 11:37:54 -0700430
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800431 bundle.putString(Contacts.Intents.Insert.NAME, name);
432 // The 0th ViewEntry in each ArrayList below is a separator item
Jeffrey Sharkey715b32a2009-03-27 18:56:05 -0700433 int entriesToAdd = Math.min(mPhoneEntries.size() - sepAdjust, PHONE_KEYS.length);
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800434 for (int x = 0; x < entriesToAdd; x++) {
Jeffrey Sharkey715b32a2009-03-27 18:56:05 -0700435 ViewEntry entry = mPhoneEntries.get(x + sepAdjust);
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800436 bundle.putString(PHONE_KEYS[x], entry.data);
437 }
Jeffrey Sharkey715b32a2009-03-27 18:56:05 -0700438 entriesToAdd = Math.min(mEmailEntries.size() - sepAdjust, EMAIL_KEYS.length);
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800439 for (int x = 0; x < entriesToAdd; x++) {
Jeffrey Sharkey715b32a2009-03-27 18:56:05 -0700440 ViewEntry entry = mEmailEntries.get(x + sepAdjust);
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800441 bundle.putString(EMAIL_KEYS[x], entry.data);
442 }
Jeffrey Sharkey715b32a2009-03-27 18:56:05 -0700443 if (mPostalEntries.size() >= 1 + sepAdjust) {
444 ViewEntry entry = mPostalEntries.get(sepAdjust);
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800445 bundle.putString(Contacts.Intents.Insert.POSTAL, entry.data);
446 }
447 intent.putExtra("ENCODE_DATA", bundle);
448 try {
449 startActivity(intent);
450 } catch (ActivityNotFoundException e) {
451 // The check in onPrepareOptionsMenu() should make this impossible, but
452 // for safety I'm catching the exception rather than crashing. Ideally
453 // I'd call Menu.removeItem() here too, but I don't see a way to get
454 // the options menu.
455 Log.e(TAG, "Show barcode menu item was clicked but Barcode Scanner " +
456 "was not installed.");
457 }
458 return true;
459 }
460 }
Evan Millar66388be2009-05-28 15:41:07 -0700461 break; */
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800462 }
463 return super.onOptionsItemSelected(item);
464 }
Evan Millar5c22c3b2009-05-29 11:37:54 -0700465
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800466 @Override
467 public boolean onContextItemSelected(MenuItem item) {
468 switch (item.getItemId()) {
469 case MENU_ITEM_MAKE_DEFAULT: {
Dmitri Plotnikovb4491ee2009-06-15 09:31:02 -0700470 if (makeItemDefault(item)) {
471 return true;
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800472 }
Dmitri Plotnikovb4491ee2009-06-15 09:31:02 -0700473 break;
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800474 }
475 }
Dmitri Plotnikovb4491ee2009-06-15 09:31:02 -0700476
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800477 return super.onContextItemSelected(item);
478 }
479
Dmitri Plotnikovb4491ee2009-06-15 09:31:02 -0700480 private boolean makeItemDefault(MenuItem item) {
481 ViewEntry entry = getViewEntryForMenuItem(item);
482 if (entry == null) {
483 return false;
484 }
485
486 // Update the primary values in the data record.
487 ContentValues values = new ContentValues(2);
488 values.put(Data.IS_PRIMARY, 1);
Dmitri Plotnikovb4491ee2009-06-15 09:31:02 -0700489
Evan Millar54a5c9f2009-06-23 17:41:09 -0700490 if (entry.ids.size() > 0) {
491 for (int i = 0; i < entry.ids.size(); i++) {
492 getContentResolver().update(ContentUris.withAppendedId(Data.CONTENT_URI,
493 entry.ids.get(i)),
494 values, null, null);
495 }
496 }
497
498 values.put(Data.IS_SUPER_PRIMARY, 1);
Dmitri Plotnikovb4491ee2009-06-15 09:31:02 -0700499 getContentResolver().update(ContentUris.withAppendedId(Data.CONTENT_URI, entry.id),
500 values, null, null);
501 dataChanged();
502 return true;
503 }
504
505 /**
506 * Shows a dialog that contains a list of all constituent contacts in this aggregate.
507 * The user picks a contact to be split into its own aggregate or clicks Cancel.
508 */
509 private void showSplitAggregateDialog() {
510
511 // Wrap this dialog in a specific theme so that list items have correct text color.
512 final ContextThemeWrapper dialogContext =
513 new ContextThemeWrapper(this, android.R.style.Theme_Light);
514 AlertDialog.Builder builder =
515 new AlertDialog.Builder(dialogContext);
516 builder.setTitle(getString(R.string.splitAggregate_title));
517
518 final SplitAggregateView view = new SplitAggregateView(dialogContext, mUri);
519 builder.setView(view);
520
521 builder.setInverseBackgroundForced(true);
522 builder.setCancelable(true);
523 builder.setNegativeButton(android.R.string.cancel,
524 new OnClickListener() {
525 public void onClick(DialogInterface dialog, int which) {
526 dialog.dismiss();
527 }
528 });
529 final AlertDialog dialog = builder.create();
530
531 view.setOnContactSelectedListener(new OnContactSelectedListener() {
532 public void onContactSelected(long contactId) {
533 dialog.dismiss();
534 splitContact(contactId);
535 }
536 });
537
538 dialog.show();
539 }
540
541 /**
Dmitri Plotnikov49f705f2009-06-17 18:31:56 -0700542 * Shows a list of aggregates that can be joined into the currently viewed aggregate.
Dmitri Plotnikovb4491ee2009-06-15 09:31:02 -0700543 */
Dmitri Plotnikov49f705f2009-06-17 18:31:56 -0700544 public void showJoinAggregateActivity() {
545 Intent intent = new Intent(ContactsListActivity.JOIN_AGGREGATE);
546 intent.putExtra(ContactsListActivity.EXTRA_AGGREGATE_ID, ContentUris.parseId(mUri));
Evan Millar8a79cee2009-08-19 17:20:49 -0700547 startActivityForResult(intent, REQUEST_JOIN_CONTACT);
Dmitri Plotnikov49f705f2009-06-17 18:31:56 -0700548 }
Dmitri Plotnikovb4491ee2009-06-15 09:31:02 -0700549
Dmitri Plotnikov49f705f2009-06-17 18:31:56 -0700550 @Override
551 protected void onActivityResult(int requestCode, int resultCode, Intent intent) {
Evan Millar8a79cee2009-08-19 17:20:49 -0700552 switch (requestCode) {
553 case REQUEST_JOIN_CONTACT: {
554 if (resultCode == RESULT_OK && intent != null) {
555 final long aggregateId = ContentUris.parseId(intent.getData());
556 joinAggregate(aggregateId);
557 }
558 break;
559 }
560 case REQUEST_EDIT_CONTACT: {
561 if (resultCode == RESULT_OK && intent != null) {
562 long newInitialSelectedRawContactId = intent.getLongExtra(
563 RAW_CONTACT_ID_EXTRA, ALL_CONTACTS_ID);
564 if (newInitialSelectedRawContactId != mSelectedRawContactId) {
565 mSelectedRawContactId = newInitialSelectedRawContactId;
566 selectInitialTab();
567 }
568 }
569 }
Dmitri Plotnikov49f705f2009-06-17 18:31:56 -0700570 }
571 }
572
573 private void splitContact(long contactId) {
574 setAggregationException(contactId, AggregationExceptions.TYPE_KEEP_OUT);
Dmitri Plotnikovd7c4af22009-06-19 18:31:00 -0700575 Toast.makeText(this, R.string.contactsSplitMessage, Toast.LENGTH_SHORT).show();
Dmitri Plotnikov49f705f2009-06-17 18:31:56 -0700576 mAdapter.notifyDataSetChanged();
577 }
578
579 private void joinAggregate(final long aggregateId) {
Dmitri Plotnikov39466592009-07-27 11:23:51 -0700580 Cursor c = mResolver.query(RawContacts.CONTENT_URI, new String[] {RawContacts._ID},
Dmitri Plotnikove1cd6792009-07-27 20:28:17 -0700581 RawContacts.CONTACT_ID + "=" + aggregateId, null, null);
Dmitri Plotnikov49f705f2009-06-17 18:31:56 -0700582
583 try {
584 while(c.moveToNext()) {
585 long contactId = c.getLong(0);
586 setAggregationException(contactId, AggregationExceptions.TYPE_KEEP_IN);
587 }
588 } finally {
589 c.close();
590 }
591
Dmitri Plotnikovd7c4af22009-06-19 18:31:00 -0700592 Toast.makeText(this, R.string.contactsJoinedMessage, Toast.LENGTH_SHORT).show();
Dmitri Plotnikov49f705f2009-06-17 18:31:56 -0700593 mAdapter.notifyDataSetChanged();
594 }
595
596 /**
597 * Given a contact ID sets an aggregation exception to either join the contact with the
598 * current aggregate or split off.
599 */
600 protected void setAggregationException(long contactId, int exceptionType) {
Dmitri Plotnikovd09f75c2009-06-16 11:59:22 -0700601 ContentValues values = new ContentValues(3);
Dmitri Plotnikove1cd6792009-07-27 20:28:17 -0700602 values.put(AggregationExceptions.CONTACT_ID, ContentUris.parseId(mUri));
Jeff Sharkey14f61ab2009-08-05 21:02:37 -0700603 values.put(AggregationExceptions.RAW_CONTACT_ID, contactId);
Dmitri Plotnikov49f705f2009-06-17 18:31:56 -0700604 values.put(AggregationExceptions.TYPE, exceptionType);
Dmitri Plotnikovd09f75c2009-06-16 11:59:22 -0700605 mResolver.update(AggregationExceptions.CONTENT_URI, values, null, null);
Dmitri Plotnikovb4491ee2009-06-15 09:31:02 -0700606 }
607
Dmitri Plotnikovef038722009-06-24 18:51:47 -0700608 private void showOptionsActivity() {
609 final Intent intent = new Intent(this, ContactOptionsActivity.class);
610 intent.setData(mUri);
611 startActivity(intent);
612 }
613
Dmitri Plotnikovb4491ee2009-06-15 09:31:02 -0700614 private ViewEntry getViewEntryForMenuItem(MenuItem item) {
615 AdapterView.AdapterContextMenuInfo info;
616 try {
617 info = (AdapterView.AdapterContextMenuInfo) item.getMenuInfo();
618 } catch (ClassCastException e) {
619 Log.e(TAG, "bad menuInfo", e);
620 return null;
621 }
622
623 return ContactEntryAdapter.getEntry(mSections, info.position, SHOW_SEPARATORS);
624 }
625
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800626 @Override
627 public boolean onKeyDown(int keyCode, KeyEvent event) {
628 switch (keyCode) {
629 case KeyEvent.KEYCODE_CALL: {
630 try {
631 ITelephony phone = ITelephony.Stub.asInterface(
632 ServiceManager.checkService("phone"));
633 if (phone != null && !phone.isIdle()) {
634 // Skip out and let the key be handled at a higher level
635 break;
636 }
637 } catch (RemoteException re) {
638 // Fall through and try to call the contact
639 }
640
Evan Millar7911ff52009-07-21 15:55:18 -0700641 int index = mListView.getSelectedItemPosition();
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800642 if (index != -1) {
643 ViewEntry entry = ViewAdapter.getEntry(mSections, index, SHOW_SEPARATORS);
Evan Millar66388be2009-05-28 15:41:07 -0700644 if (entry.intent.getAction() == Intent.ACTION_CALL_PRIVILEGED) {
645 startActivity(entry.intent);
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800646 }
647 } else if (mNumPhoneNumbers != 0) {
648 // There isn't anything selected, call the default number
649 Intent intent = new Intent(Intent.ACTION_CALL_PRIVILEGED, mUri);
650 startActivity(intent);
651 }
652 return true;
653 }
654
655 case KeyEvent.KEYCODE_DEL: {
656 showDialog(DIALOG_CONFIRM_DELETE);
657 return true;
658 }
659 }
660
661 return super.onKeyDown(keyCode, event);
662 }
663
Evan Millar7911ff52009-07-21 15:55:18 -0700664 public void onItemClick(AdapterView parent, View v, int position, long id) {
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800665 ViewEntry entry = ViewAdapter.getEntry(mSections, position, SHOW_SEPARATORS);
666 if (entry != null) {
667 Intent intent = entry.intent;
668 if (intent != null) {
669 try {
670 startActivity(intent);
671 } catch (ActivityNotFoundException e) {
672 Log.e(TAG, "No activity found for intent: " + intent);
673 signalError();
674 }
675 } else {
676 signalError();
677 }
678 } else {
679 signalError();
680 }
681 }
682
683 /**
684 * Signal an error to the user via a beep, or some other method.
685 */
686 private void signalError() {
687 //TODO: implement this when we have the sonification APIs
688 }
689
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800690 private Uri constructImToUrl(String host, String data) {
Evan Millar45e0ed32009-06-01 16:44:38 -0700691 // don't encode the url, because the Activity Manager can't find using the encoded url
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800692 StringBuilder buf = new StringBuilder("imto://");
693 buf.append(host);
694 buf.append('/');
695 buf.append(data);
696 return Uri.parse(buf.toString());
697 }
698
699 /**
700 * Build up the entries to display on the screen.
Evan Millar5c22c3b2009-05-29 11:37:54 -0700701 *
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800702 * @param personCursor the URI for the contact being displayed
703 */
Evan Millar66388be2009-05-28 15:41:07 -0700704 private final void buildEntries(Cursor aggCursor) {
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800705 // Clear out the old entries
706 final int numSections = mSections.size();
707 for (int i = 0; i < numSections; i++) {
708 mSections.get(i).clear();
709 }
710
Evan Millar7911ff52009-07-21 15:55:18 -0700711 mRawContactIds.clear();
Dmitri Plotnikovb4491ee2009-06-15 09:31:02 -0700712
Evan Millar66388be2009-05-28 15:41:07 -0700713 // Build up method entries
714 if (mUri != null) {
Jeff Hamilton119b6ca2009-07-30 12:57:49 -0500715 aggCursor.moveToPosition(-1);
Evan Millar66388be2009-05-28 15:41:07 -0700716 while (aggCursor.moveToNext()) {
717 final String mimetype = aggCursor.getString(DATA_MIMETYPE_COLUMN);
Evan Millar5c22c3b2009-05-29 11:37:54 -0700718
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800719 ViewEntry entry = new ViewEntry();
Evan Millar66388be2009-05-28 15:41:07 -0700720
721 final long id = aggCursor.getLong(DATA_ID_COLUMN);
722 final Uri uri = ContentUris.withAppendedId(Data.CONTENT_URI, id);
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800723 entry.id = id;
724 entry.uri = uri;
Evan Millar45e0ed32009-06-01 16:44:38 -0700725 entry.mimetype = mimetype;
Evan Millar7911ff52009-07-21 15:55:18 -0700726 // TODO: entry.contactId should be renamed to entry.rawContactId
Dmitri Plotnikovb4491ee2009-06-15 09:31:02 -0700727 entry.contactId = aggCursor.getLong(DATA_CONTACT_ID_COLUMN);
Evan Millar7911ff52009-07-21 15:55:18 -0700728 if (!mRawContactIds.contains(entry.contactId)) {
729 mRawContactIds.add(entry.contactId);
730 }
731
732 // This performs the tab filtering
Evan Millar8a79cee2009-08-19 17:20:49 -0700733 if (mSelectedRawContactId != entry.contactId
734 && mSelectedRawContactId != ALL_CONTACTS_ID) {
Evan Millar7911ff52009-07-21 15:55:18 -0700735 continue;
Dmitri Plotnikovb4491ee2009-06-15 09:31:02 -0700736 }
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800737
Evan Millar66388be2009-05-28 15:41:07 -0700738 if (mimetype.equals(CommonDataKinds.Phone.CONTENT_ITEM_TYPE)
Evan Millar5c22c3b2009-05-29 11:37:54 -0700739 || mimetype.equals(CommonDataKinds.Email.CONTENT_ITEM_TYPE)
Jeff Sharkeyc6ad3ab2009-07-21 19:30:15 -0700740 || mimetype.equals(CommonDataKinds.StructuredPostal.CONTENT_ITEM_TYPE)
Evan Millar66388be2009-05-28 15:41:07 -0700741 || mimetype.equals(CommonDataKinds.Im.CONTENT_ITEM_TYPE)) {
742 final int type = aggCursor.getInt(DATA_1_COLUMN);
Evan Millar45e0ed32009-06-01 16:44:38 -0700743 final String label = aggCursor.getString(DATA_3_COLUMN);
744 final String data = aggCursor.getString(DATA_2_COLUMN);
745 final boolean isSuperPrimary = "1".equals(
746 aggCursor.getString(DATA_IS_SUPER_PRIMARY_COLUMN));
Evan Millar66388be2009-05-28 15:41:07 -0700747
Evan Millar54a5c9f2009-06-23 17:41:09 -0700748 entry.type = type;
749
Evan Millar66388be2009-05-28 15:41:07 -0700750 // Don't crash if the data is bogus
751 if (TextUtils.isEmpty(data)) {
752 Log.w(TAG, "empty data for contact method " + id);
753 continue;
754 }
755
756 // Build phone entries
757 if (mimetype.equals(CommonDataKinds.Phone.CONTENT_ITEM_TYPE)) {
758 mNumPhoneNumbers++;
Evan Millar5c22c3b2009-05-29 11:37:54 -0700759
Evan Millar66388be2009-05-28 15:41:07 -0700760 final CharSequence displayLabel = ContactsUtils.getDisplayLabel(
761 this, mimetype, type, label);
762 entry.label = buildActionString(R.string.actionCall, displayLabel, true);
Evan Millar54a5c9f2009-06-23 17:41:09 -0700763 entry.data = PhoneNumberUtils.stripSeparators(data);
Evan Millar296758b2009-08-07 15:40:08 -0700764 entry.intent = new Intent(Intent.ACTION_CALL_PRIVILEGED,
765 Uri.fromParts("tel", data, null));
Evan Millar15e514d2009-08-04 10:14:57 -0700766 entry.secondaryIntent = new Intent(Intent.ACTION_SENDTO,
Evan Millar66388be2009-05-28 15:41:07 -0700767 Uri.fromParts("sms", data, null));
Evan Millar15e514d2009-08-04 10:14:57 -0700768 entry.isPrimary = isSuperPrimary;
Evan Millar66388be2009-05-28 15:41:07 -0700769 entry.actionIcon = android.R.drawable.sym_action_call;
770 mPhoneEntries.add(entry);
771
Evan Millar5c22c3b2009-05-29 11:37:54 -0700772 if (type == CommonDataKinds.Phone.TYPE_MOBILE
Evan Millar66388be2009-05-28 15:41:07 -0700773 || mShowSmsLinksForAllPhones) {
774 // Add an SMS entry
Evan Millar15e514d2009-08-04 10:14:57 -0700775 entry.secondaryActionIcon = R.drawable.sym_action_sms;
Evan Millar66388be2009-05-28 15:41:07 -0700776 }
777 // Build email entries
778 } else if (mimetype.equals(CommonDataKinds.Email.CONTENT_ITEM_TYPE)) {
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800779 entry.label = buildActionString(R.string.actionEmail,
Evan Millar66388be2009-05-28 15:41:07 -0700780 ContactsUtils.getDisplayLabel(this, mimetype, type, label), true);
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800781 entry.data = data;
782 entry.intent = new Intent(Intent.ACTION_SENDTO,
783 Uri.fromParts("mailto", data, null));
784 entry.actionIcon = android.R.drawable.sym_action_email;
Evan Millar15e514d2009-08-04 10:14:57 -0700785 entry.isPrimary = isSuperPrimary;
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800786 mEmailEntries.add(entry);
Evan Millar66388be2009-05-28 15:41:07 -0700787 // Build postal entries
Jeff Sharkeyc6ad3ab2009-07-21 19:30:15 -0700788 } else if (mimetype.equals(CommonDataKinds.StructuredPostal.CONTENT_ITEM_TYPE)) {
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800789 entry.label = buildActionString(R.string.actionMap,
Evan Millar66388be2009-05-28 15:41:07 -0700790 ContactsUtils.getDisplayLabel(this, mimetype, type, label), true);
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800791 entry.data = data;
792 entry.maxLines = 4;
793 entry.intent = new Intent(Intent.ACTION_VIEW, uri);
794 entry.actionIcon = R.drawable.sym_action_map;
795 mPostalEntries.add(entry);
Evan Millar66388be2009-05-28 15:41:07 -0700796 // Build im entries
797 } else if (mimetype.equals(CommonDataKinds.Im.CONTENT_ITEM_TYPE)) {
798 String[] protocolStrings = getResources().getStringArray(
799 android.R.array.imProtocols);
Jeff Sharkeye731d422009-07-28 21:12:43 -0700800 Object protocolObj = aggCursor.getString(DATA_5_COLUMN);
Alex Kennberg87fc3172009-03-28 06:43:06 -0700801 String host = null;
Jeff Sharkeye731d422009-07-28 21:12:43 -0700802 // TODO: fix by moving to contactssource-based rendering rules
803// Object protocolObj = ContactsUtils.decodeImProtocol(
804// aggCursor.getString(DATA_5_COLUMN));
805// if (protocolObj instanceof Number) {
806// int protocol = ((Number) protocolObj).intValue();
807// entry.label = buildActionString(R.string.actionChat,
808// protocolStrings[protocol], false);
809// host = ContactsUtils.lookupProviderNameFromId(
810// protocol).toLowerCase();
811// if (protocol == CommonDataKinds.Im.PROTOCOL_GOOGLE_TALK
812// || protocol == CommonDataKinds.Im.PROTOCOL_MSN) {
813// entry.maxLabelLines = 2;
814// }
Neel Parekh064fa802009-08-12 12:57:12 -0700815 if (protocolObj != null) {
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800816 String providerName = (String) protocolObj;
817 entry.label = buildActionString(R.string.actionChat,
818 providerName, false);
819 host = providerName.toLowerCase();
Neel Parekh064fa802009-08-12 12:57:12 -0700820 }
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800821
822 // Only add the intent if there is a valid host
823 if (!TextUtils.isEmpty(host)) {
824 entry.intent = new Intent(Intent.ACTION_SENDTO,
825 constructImToUrl(host, data));
826 }
827 entry.data = data;
Evan Millar54a5c9f2009-06-23 17:41:09 -0700828 //TODO(emillar) Add in presence info
Evan Millar66388be2009-05-28 15:41:07 -0700829 /*if (!aggCursor.isNull(METHODS_STATUS_COLUMN)) {
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800830 entry.presenceIcon = Presence.getPresenceIconResourceId(
Evan Millar66388be2009-05-28 15:41:07 -0700831 aggCursor.getInt(METHODS_STATUS_COLUMN));
Evan Millar54a5c9f2009-06-23 17:41:09 -0700832 entry.status = ...
Evan Millar66388be2009-05-28 15:41:07 -0700833 }*/
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800834 entry.actionIcon = android.R.drawable.sym_action_chat;
835 mImEntries.add(entry);
Evan Millar5c22c3b2009-05-29 11:37:54 -0700836 }
Evan Millar66388be2009-05-28 15:41:07 -0700837 // Build organization entries
838 } else if (mimetype.equals(CommonDataKinds.Organization.CONTENT_ITEM_TYPE)) {
Evan Millar66388be2009-05-28 15:41:07 -0700839 final String company = aggCursor.getString(DATA_3_COLUMN);
840 final String title = aggCursor.getString(DATA_4_COLUMN);
Evan Millar5c22c3b2009-05-29 11:37:54 -0700841
Evan Millar66388be2009-05-28 15:41:07 -0700842 // Don't crash if the data is bogus
843 if (TextUtils.isEmpty(company) && TextUtils.isEmpty(title)) {
844 Log.w(TAG, "empty data for contact method " + id);
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800845 continue;
846 }
Evan Millar5c22c3b2009-05-29 11:37:54 -0700847
Evan Millar66388be2009-05-28 15:41:07 -0700848 entry.data = title;
849 entry.actionIcon = R.drawable.sym_action_organization;
850 entry.label = company;
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800851
Evan Millar66388be2009-05-28 15:41:07 -0700852 mOrganizationEntries.add(entry);
853 // Build note entries
854 } else if (mimetype.equals(CommonDataKinds.Note.CONTENT_ITEM_TYPE)) {
855 entry.label = getString(R.string.label_notes);
856 entry.data = aggCursor.getString(DATA_1_COLUMN);
857 entry.id = 0;
858 entry.uri = null;
859 entry.intent = null;
860 entry.maxLines = 10;
861 entry.actionIcon = R.drawable.sym_note;
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800862
Evan Millar66388be2009-05-28 15:41:07 -0700863 if (TextUtils.isEmpty(entry.data)) {
864 Log.w(TAG, "empty data for contact method " + id);
Alex Kennberg87fc3172009-03-28 06:43:06 -0700865 continue;
866 }
Evan Millar5c22c3b2009-05-29 11:37:54 -0700867
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800868 mOtherEntries.add(entry);
Evan Millar66388be2009-05-28 15:41:07 -0700869 }
Evan Millar5c22c3b2009-05-29 11:37:54 -0700870
Evan Millar45e0ed32009-06-01 16:44:38 -0700871
Evan Millar66388be2009-05-28 15:41:07 -0700872 // TODO(emillar) Add group entries
873// // Build the group entries
874// final Uri groupsUri = Uri.withAppendedPath(mUri, GroupMembership.CONTENT_DIRECTORY);
875// Cursor groupCursor = mResolver.query(groupsUri, ContactsListActivity.GROUPS_PROJECTION,
876// null, null, Groups.DEFAULT_SORT_ORDER);
877// if (groupCursor != null) {
878// try {
879// StringBuilder sb = new StringBuilder();
880//
881// while (groupCursor.moveToNext()) {
882// String systemId = groupCursor.getString(
883// ContactsListActivity.GROUPS_COLUMN_INDEX_SYSTEM_ID);
884//
885// if (systemId != null || Groups.GROUP_MY_CONTACTS.equals(systemId)) {
886// continue;
887// }
888//
889// String name = groupCursor.getString(ContactsListActivity.GROUPS_COLUMN_INDEX_NAME);
890// if (!TextUtils.isEmpty(name)) {
891// if (sb.length() == 0) {
892// sb.append(name);
893// } else {
894// sb.append(getString(R.string.group_list, name));
895// }
896// }
897// }
898//
899// if (sb.length() > 0) {
900// ViewEntry entry = new ViewEntry();
901// entry.kind = ContactEntryAdapter.Entry.KIND_GROUP;
902// entry.label = getString(R.string.label_groups);
903// entry.data = sb.toString();
904// entry.intent = new Intent(Intent.ACTION_EDIT, mUri);
905//
906// // TODO: Add an icon for the groups item.
907//
908// mGroupEntries.add(entry);
909// }
910// } finally {
911// groupCursor.close();
912// }
913// }
Evan Millar5c22c3b2009-05-29 11:37:54 -0700914
Evan Millar66388be2009-05-28 15:41:07 -0700915 }
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800916 }
917 }
918
919 String buildActionString(int actionResId, CharSequence type, boolean lowerCase) {
Jeff Hamilton8350e5b2009-03-24 21:01:34 -0700920 // If there is no type just display an empty string
921 if (type == null) {
922 type = "";
923 }
924
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800925 if (lowerCase) {
926 return getString(actionResId, type.toString().toLowerCase());
927 } else {
928 return getString(actionResId, type.toString());
929 }
930 }
Evan Millar5c22c3b2009-05-29 11:37:54 -0700931
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800932 /**
933 * A basic structure with the data for a contact entry in the list.
934 */
Evan Millar54a5c9f2009-06-23 17:41:09 -0700935 static class ViewEntry extends ContactEntryAdapter.Entry implements Collapsible<ViewEntry> {
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800936 public int actionIcon = -1;
Evan Millar15e514d2009-08-04 10:14:57 -0700937 public boolean isPrimary = false;
938 public int presenceIcon = -1;
939 public int secondaryActionIcon = -1;
940 public Intent intent;
941 public Intent secondaryIntent = null;
942 public int status = -1;
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800943 public int maxLabelLines = 1;
Evan Millar54a5c9f2009-06-23 17:41:09 -0700944 public ArrayList<Long> ids = new ArrayList<Long>();
945 public int collapseCount = 0;
946
947 public boolean collapseWith(ViewEntry entry) {
948 // assert equal collapse keys
949 if (!getCollapseKey().equals(entry.getCollapseKey())) {
950 return false;
951 }
952
953 // Choose the label associated with the highest type precedence.
954 if (TypePrecedence.getTypePrecedence(mimetype, type)
955 > TypePrecedence.getTypePrecedence(entry.mimetype, entry.type)) {
956 type = entry.type;
957 label = entry.label;
958 }
959
960 // Choose the max of the maxLines and maxLabelLines values.
961 maxLines = Math.max(maxLines, entry.maxLines);
962 maxLabelLines = Math.max(maxLabelLines, entry.maxLabelLines);
963
964 // Choose the presence with the highest precedence.
965 if (Presence.getPresencePrecedence(status)
966 < Presence.getPresencePrecedence(entry.status)) {
967 status = entry.status;
968 }
969
970 // If any of the collapsed entries are primary make the whole thing primary.
Evan Millar15e514d2009-08-04 10:14:57 -0700971 isPrimary = entry.isPrimary ? true : isPrimary;
Evan Millar54a5c9f2009-06-23 17:41:09 -0700972
973 // uri, and contactdId, shouldn't make a difference. Just keep the original.
974
975 // Keep track of all the ids that have been collapsed with this one.
976 ids.add(entry.id);
977 collapseCount++;
978 return true;
979 }
980
981 public String getCollapseKey() {
982 StringBuilder hashSb = new StringBuilder();
983 hashSb.append(data);
984 hashSb.append(mimetype);
985 hashSb.append((intent != null && intent.getAction() != null)
986 ? intent.getAction() : "");
Evan Millar15e514d2009-08-04 10:14:57 -0700987 hashSb.append((secondaryIntent != null && secondaryIntent.getAction() != null)
988 ? secondaryIntent.getAction() : "");
Evan Millar54a5c9f2009-06-23 17:41:09 -0700989 hashSb.append(actionIcon);
990 return hashSb.toString();
991 }
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800992 }
993
Evan Millar15e514d2009-08-04 10:14:57 -0700994 /** Cache of the children views of a row */
995 static class ViewCache {
996 public TextView label;
997 public TextView data;
998 public ImageView actionIcon;
999 public ImageView presenceIcon;
1000 public ImageView primaryIcon;
1001 public ImageView secondaryActionButton;
1002 public View secondaryActionDivider;
Evan Millar5c22c3b2009-05-29 11:37:54 -07001003
Evan Millar15e514d2009-08-04 10:14:57 -07001004 // Need to keep track of this too
1005 ViewEntry entry;
1006 }
1007
1008 private final class ViewAdapter extends ContactEntryAdapter<ViewEntry>
1009 implements View.OnClickListener {
1010
Evan Millar5c22c3b2009-05-29 11:37:54 -07001011
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08001012 ViewAdapter(Context context, ArrayList<ArrayList<ViewEntry>> sections) {
1013 super(context, sections, SHOW_SEPARATORS);
1014 }
1015
Evan Millar15e514d2009-08-04 10:14:57 -07001016 public void onClick(View v) {
1017 Intent intent = (Intent) v.getTag();
1018 startActivity(intent);
1019 }
1020
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08001021 @Override
1022 public View getView(int position, View convertView, ViewGroup parent) {
Evan Millar5c22c3b2009-05-29 11:37:54 -07001023 ViewEntry entry = getEntry(mSections, position, false);
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08001024 View v;
1025
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08001026 ViewCache views;
1027
1028 // Check to see if we can reuse convertView
1029 if (convertView != null) {
1030 v = convertView;
1031 views = (ViewCache) v.getTag();
1032 } else {
1033 // Create a new view if needed
1034 v = mInflater.inflate(R.layout.list_item_text_icons, parent, false);
1035
1036 // Cache the children
1037 views = new ViewCache();
1038 views.label = (TextView) v.findViewById(android.R.id.text1);
1039 views.data = (TextView) v.findViewById(android.R.id.text2);
Evan Millar15e514d2009-08-04 10:14:57 -07001040 views.actionIcon = (ImageView) v.findViewById(R.id.action_icon);
1041 views.primaryIcon = (ImageView) v.findViewById(R.id.primary_icon);
1042 views.presenceIcon = (ImageView) v.findViewById(R.id.presence_icon);
1043 views.secondaryActionButton = (ImageView) v.findViewById(
1044 R.id.secondary_action_button);
1045 views.secondaryActionButton.setOnClickListener(this);
Evan Millar15e514d2009-08-04 10:14:57 -07001046 views.secondaryActionDivider = v.findViewById(R.id.divider);
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08001047 v.setTag(views);
1048 }
1049
1050 // Update the entry in the view cache
1051 views.entry = entry;
1052
1053 // Bind the data to the view
1054 bindView(v, entry);
1055 return v;
1056 }
1057
1058 @Override
1059 protected View newView(int position, ViewGroup parent) {
1060 // getView() handles this
1061 throw new UnsupportedOperationException();
1062 }
1063
1064 @Override
1065 protected void bindView(View view, ViewEntry entry) {
1066 final Resources resources = mContext.getResources();
1067 ViewCache views = (ViewCache) view.getTag();
1068
1069 // Set the label
1070 TextView label = views.label;
1071 setMaxLines(label, entry.maxLabelLines);
1072 label.setText(entry.label);
1073
1074 // Set the data
1075 TextView data = views.data;
1076 if (data != null) {
Evan Millar54a5c9f2009-06-23 17:41:09 -07001077 if (entry.mimetype.equals(Phone.CONTENT_ITEM_TYPE)
1078 || entry.mimetype.equals(FastTrackWindow.MIME_SMS_ADDRESS)) {
1079 data.setText(PhoneNumberUtils.formatNumber(entry.data));
1080 } else {
1081 data.setText(entry.data);
1082 }
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08001083 setMaxLines(data, entry.maxLines);
1084 }
1085
Evan Millar15e514d2009-08-04 10:14:57 -07001086 // Set the primary icon
1087 views.primaryIcon.setVisibility(entry.isPrimary ? View.VISIBLE : View.GONE);
1088
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08001089 // Set the action icon
1090 ImageView action = views.actionIcon;
1091 if (entry.actionIcon != -1) {
1092 action.setImageDrawable(resources.getDrawable(entry.actionIcon));
1093 action.setVisibility(View.VISIBLE);
1094 } else {
1095 // Things should still line up as if there was an icon, so make it invisible
1096 action.setVisibility(View.INVISIBLE);
1097 }
1098
1099 // Set the presence icon
1100 Drawable presenceIcon = null;
Evan Millar15e514d2009-08-04 10:14:57 -07001101 if (entry.presenceIcon != -1) {
1102 presenceIcon = resources.getDrawable(entry.presenceIcon);
Evan Millar54a5c9f2009-06-23 17:41:09 -07001103 } else if (entry.status != -1) {
1104 presenceIcon = resources.getDrawable(
1105 Presence.getPresenceIconResourceId(entry.status));
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08001106 }
Evan Millar15e514d2009-08-04 10:14:57 -07001107 ImageView presenceIconView = views.presenceIcon;
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08001108 if (presenceIcon != null) {
Evan Millar15e514d2009-08-04 10:14:57 -07001109 presenceIconView.setImageDrawable(presenceIcon);
1110 presenceIconView.setVisibility(View.VISIBLE);
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08001111 } else {
Evan Millar15e514d2009-08-04 10:14:57 -07001112 presenceIconView.setVisibility(View.GONE);
1113 }
1114
1115 // Set the secondary action button
1116 ImageView secondaryActionView = views.secondaryActionButton;
1117 Drawable secondaryActionIcon = null;
1118 if (entry.secondaryActionIcon != -1) {
1119 secondaryActionIcon = resources.getDrawable(entry.secondaryActionIcon);
1120 }
1121 if (entry.secondaryIntent != null && secondaryActionIcon != null) {
1122 secondaryActionView.setImageDrawable(secondaryActionIcon);
1123 secondaryActionView.setTag(entry.secondaryIntent);
1124 secondaryActionView.setVisibility(View.VISIBLE);
1125 views.secondaryActionDivider.setVisibility(View.VISIBLE);
1126 } else {
1127 secondaryActionView.setVisibility(View.GONE);
1128 views.secondaryActionDivider.setVisibility(View.GONE);
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08001129 }
1130 }
1131
1132 private void setMaxLines(TextView textView, int maxLines) {
1133 if (maxLines == 1) {
1134 textView.setSingleLine(true);
1135 textView.setEllipsize(TextUtils.TruncateAt.END);
1136 } else {
1137 textView.setSingleLine(false);
1138 textView.setMaxLines(maxLines);
1139 textView.setEllipsize(null);
1140 }
1141 }
1142 }
1143}