blob: c6fd7eba01ed6eb64db4e30be732ea57df5fb7e0 [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 Millar66388be2009-05-28 15:41:07 -070019import static com.android.contacts.ContactEntryAdapter.AGGREGATE_PROJECTION;
20import static com.android.contacts.ContactEntryAdapter.AGGREGATE_STARRED_COLUMN;
Evan Millar66388be2009-05-28 15:41:07 -070021import static com.android.contacts.ContactEntryAdapter.DATA_1_COLUMN;
22import static com.android.contacts.ContactEntryAdapter.DATA_2_COLUMN;
23import static com.android.contacts.ContactEntryAdapter.DATA_3_COLUMN;
24import static com.android.contacts.ContactEntryAdapter.DATA_4_COLUMN;
25import static com.android.contacts.ContactEntryAdapter.DATA_5_COLUMN;
Evan Millar66388be2009-05-28 15:41:07 -070026import static com.android.contacts.ContactEntryAdapter.DATA_9_COLUMN;
Dmitri Plotnikovb4491ee2009-06-15 09:31:02 -070027import static com.android.contacts.ContactEntryAdapter.DATA_CONTACT_ID_COLUMN;
28import static com.android.contacts.ContactEntryAdapter.DATA_ID_COLUMN;
29import static com.android.contacts.ContactEntryAdapter.DATA_IS_SUPER_PRIMARY_COLUMN;
30import static com.android.contacts.ContactEntryAdapter.DATA_MIMETYPE_COLUMN;
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -080031
Dmitri Plotnikovb4491ee2009-06-15 09:31:02 -070032import com.android.contacts.SplitAggregateView.OnContactSelectedListener;
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -080033import com.android.internal.telephony.ITelephony;
34
35import android.app.AlertDialog;
36import android.app.Dialog;
37import android.app.ListActivity;
38import android.content.ActivityNotFoundException;
39import android.content.ContentResolver;
40import android.content.ContentUris;
41import android.content.ContentValues;
42import android.content.Context;
43import android.content.DialogInterface;
44import android.content.Intent;
Dmitri Plotnikovb4491ee2009-06-15 09:31:02 -070045import android.content.DialogInterface.OnClickListener;
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -080046import android.content.pm.PackageManager;
47import android.content.pm.ResolveInfo;
48import android.content.res.Resources;
49import android.database.ContentObserver;
50import android.database.Cursor;
Evan Millar45e0ed32009-06-01 16:44:38 -070051import android.graphics.Bitmap;
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -080052import android.graphics.drawable.Drawable;
53import android.media.Ringtone;
54import android.media.RingtoneManager;
55import android.net.Uri;
56import android.os.Bundle;
57import android.os.Handler;
58import android.os.RemoteException;
59import android.os.ServiceManager;
60import android.os.SystemClock;
Dmitri Plotnikov49f705f2009-06-17 18:31:56 -070061import android.provider.ContactsContract.Contacts;
Evan Millar66388be2009-05-28 15:41:07 -070062import android.provider.ContactsContract.Aggregates;
Dmitri Plotnikovb4491ee2009-06-15 09:31:02 -070063import android.provider.ContactsContract.AggregationExceptions;
64import android.provider.ContactsContract.CommonDataKinds;
Evan Millar66388be2009-05-28 15:41:07 -070065import android.provider.ContactsContract.Data;
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -080066import android.text.TextUtils;
67import android.util.Log;
68import android.view.ContextMenu;
Dmitri Plotnikovb4491ee2009-06-15 09:31:02 -070069import android.view.ContextThemeWrapper;
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -080070import android.view.KeyEvent;
71import android.view.Menu;
72import android.view.MenuItem;
73import android.view.View;
74import android.view.ViewGroup;
75import android.view.ContextMenu.ContextMenuInfo;
76import android.widget.AdapterView;
77import android.widget.CheckBox;
78import android.widget.ImageView;
79import android.widget.ListView;
80import android.widget.TextView;
81import android.widget.Toast;
82
83import java.util.ArrayList;
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -080084
85/**
86 * Displays the details of a specific contact.
87 */
Evan Millar5c22c3b2009-05-29 11:37:54 -070088public class ViewContactActivity extends ListActivity
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -080089 implements View.OnCreateContextMenuListener, View.OnClickListener,
90 DialogInterface.OnClickListener {
91 private static final String TAG = "ViewContact";
92 private static final String SHOW_BARCODE_INTENT = "com.google.zxing.client.android.ENCODE";
93
94 private static final boolean SHOW_SEPARATORS = false;
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -080095
96 private static final int DIALOG_CONFIRM_DELETE = 1;
97
Dmitri Plotnikov49f705f2009-06-17 18:31:56 -070098 private static final int REQUEST_JOIN_AGGREGATE = 1;
99
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800100 public static final int MENU_ITEM_DELETE = 1;
101 public static final int MENU_ITEM_MAKE_DEFAULT = 2;
102 public static final int MENU_ITEM_SHOW_BARCODE = 3;
Dmitri Plotnikovb4491ee2009-06-15 09:31:02 -0700103 public static final int MENU_ITEM_SPLIT_AGGREGATE = 4;
Dmitri Plotnikov49f705f2009-06-17 18:31:56 -0700104 public static final int MENU_ITEM_JOIN_AGGREGATE = 5;
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800105
106 private Uri mUri;
Evan Millar45e0ed32009-06-01 16:44:38 -0700107 private Uri mAggDataUri;
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800108 private ContentResolver mResolver;
109 private ViewAdapter mAdapter;
110 private int mNumPhoneNumbers = 0;
111
Dmitri Plotnikovb4491ee2009-06-15 09:31:02 -0700112 /**
113 * A list of distinct contact IDs included in the current aggregate.
114 */
115 private ArrayList<Long> mContactIds = new ArrayList<Long>();
116
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800117 /* package */ ArrayList<ViewEntry> mPhoneEntries = new ArrayList<ViewEntry>();
118 /* package */ ArrayList<ViewEntry> mSmsEntries = new ArrayList<ViewEntry>();
119 /* package */ ArrayList<ViewEntry> mEmailEntries = new ArrayList<ViewEntry>();
120 /* package */ ArrayList<ViewEntry> mPostalEntries = new ArrayList<ViewEntry>();
121 /* package */ ArrayList<ViewEntry> mImEntries = new ArrayList<ViewEntry>();
122 /* package */ ArrayList<ViewEntry> mOrganizationEntries = new ArrayList<ViewEntry>();
The Android Open Source Projectcac191e2009-03-18 22:20:27 -0700123 /* package */ ArrayList<ViewEntry> mGroupEntries = new ArrayList<ViewEntry>();
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800124 /* package */ ArrayList<ViewEntry> mOtherEntries = new ArrayList<ViewEntry>();
125 /* package */ ArrayList<ArrayList<ViewEntry>> mSections = new ArrayList<ArrayList<ViewEntry>>();
126
127 private Cursor mCursor;
128 private boolean mObserverRegistered;
Evan Millar5c22c3b2009-05-29 11:37:54 -0700129
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800130 private ContentObserver mObserver = new ContentObserver(new Handler()) {
131 @Override
132 public boolean deliverSelfNotifications() {
133 return true;
134 }
135
136 @Override
137 public void onChange(boolean selfChange) {
138 if (mCursor != null && !mCursor.isClosed()){
139 dataChanged();
140 }
141 }
142 };
143
144 public void onClick(DialogInterface dialog, int which) {
145 if (mCursor != null) {
146 if (mObserverRegistered) {
147 mCursor.unregisterContentObserver(mObserver);
148 mObserverRegistered = false;
149 }
150 mCursor.close();
151 mCursor = null;
152 }
153 getContentResolver().delete(mUri, null, null);
154 finish();
155 }
156
157 public void onClick(View view) {
158 if (!mObserverRegistered) {
159 return;
160 }
161 switch (view.getId()) {
162 case R.id.star: {
Evan Millar7e4accf2009-06-08 10:43:26 -0700163 mCursor.moveToFirst();
Evan Millar66388be2009-05-28 15:41:07 -0700164 int oldStarredState = mCursor.getInt(AGGREGATE_STARRED_COLUMN);
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800165 ContentValues values = new ContentValues(1);
Evan Millar66388be2009-05-28 15:41:07 -0700166 values.put(Aggregates.STARRED, oldStarredState == 1 ? 0 : 1);
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800167 getContentResolver().update(mUri, values, null, null);
168 break;
169 }
170 }
171 }
172
173 private TextView mNameView;
174 private TextView mPhoneticNameView; // may be null in some locales
175 private ImageView mPhotoView;
176 private int mNoPhotoResource;
177 private CheckBox mStarView;
178 private boolean mShowSmsLinksForAllPhones;
179
180 @Override
181 protected void onCreate(Bundle icicle) {
182 super.onCreate(icicle);
183
184 setContentView(R.layout.view_contact);
185 getListView().setOnCreateContextMenuListener(this);
186
187 mNameView = (TextView) findViewById(R.id.name);
188 mPhoneticNameView = (TextView) findViewById(R.id.phonetic_name);
189 mPhotoView = (ImageView) findViewById(R.id.photo);
190 mStarView = (CheckBox) findViewById(R.id.star);
191 mStarView.setOnClickListener(this);
192
193 // Set the photo with a random "no contact" image
194 long now = SystemClock.elapsedRealtime();
195 int num = (int) now & 0xf;
196 if (num < 9) {
197 // Leaning in from right, common
198 mNoPhotoResource = R.drawable.ic_contact_picture;
199 } else if (num < 14) {
200 // Leaning in from left uncommon
201 mNoPhotoResource = R.drawable.ic_contact_picture_2;
202 } else {
203 // Coming in from the top, rare
204 mNoPhotoResource = R.drawable.ic_contact_picture_3;
205 }
206
Evan Millar45e0ed32009-06-01 16:44:38 -0700207 mUri = getIntent().getData();
208 mAggDataUri = Uri.withAppendedPath(mUri, "data");
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;
224
Evan Millar45e0ed32009-06-01 16:44:38 -0700225 mCursor = mResolver.query(mAggDataUri,
226 AGGREGATE_PROJECTION, null, null, null);
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800227 }
228
229 @Override
230 protected void onResume() {
231 super.onResume();
232 mObserverRegistered = true;
233 mCursor.registerContentObserver(mObserver);
234 dataChanged();
235 }
236
237 @Override
238 protected void onPause() {
239 super.onPause();
240 if (mCursor != null) {
241 if (mObserverRegistered) {
242 mObserverRegistered = false;
243 mCursor.unregisterContentObserver(mObserver);
244 }
245 mCursor.deactivate();
246 }
247 }
248
249 @Override
250 protected void onDestroy() {
251 super.onDestroy();
252
253 if (mCursor != null) {
254 if (mObserverRegistered) {
255 mCursor.unregisterContentObserver(mObserver);
256 mObserverRegistered = false;
257 }
258 mCursor.close();
259 }
260 }
261
262 @Override
263 protected Dialog onCreateDialog(int id) {
264 switch (id) {
265 case DIALOG_CONFIRM_DELETE:
266 return new AlertDialog.Builder(this)
267 .setTitle(R.string.deleteConfirmation_title)
268 .setIcon(android.R.drawable.ic_dialog_alert)
269 .setMessage(R.string.deleteConfirmation)
270 .setNegativeButton(android.R.string.cancel, null)
271 .setPositiveButton(android.R.string.ok, this)
272 .setCancelable(false)
273 .create();
274 }
275 return null;
276 }
277
278 private void dataChanged() {
279 mCursor.requery();
280 if (mCursor.moveToFirst()) {
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800281 // Set the star
Evan Millar66388be2009-05-28 15:41:07 -0700282 mStarView.setChecked(mCursor.getInt(AGGREGATE_STARRED_COLUMN) == 1 ? true : false);
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800283
284 // Build up the contact entries
285 buildEntries(mCursor);
286 if (mAdapter == null) {
287 mAdapter = new ViewAdapter(this, mSections);
288 setListAdapter(mAdapter);
289 } else {
290 mAdapter.setSections(mSections, SHOW_SEPARATORS);
291 }
292 } else {
293 Toast.makeText(this, R.string.invalidContactMessage, Toast.LENGTH_SHORT).show();
294 Log.e(TAG, "invalid contact uri: " + mUri);
295 finish();
296 }
297 }
298
299 @Override
300 public boolean onCreateOptionsMenu(Menu menu) {
301 menu.add(0, 0, 0, R.string.menu_editContact)
302 .setIcon(android.R.drawable.ic_menu_edit)
303 .setIntent(new Intent(Intent.ACTION_EDIT, mUri))
304 .setAlphabeticShortcut('e');
305 menu.add(0, MENU_ITEM_DELETE, 0, R.string.menu_deleteContact)
306 .setIcon(android.R.drawable.ic_menu_delete);
Dmitri Plotnikovb4491ee2009-06-15 09:31:02 -0700307 menu.add(0, MENU_ITEM_SPLIT_AGGREGATE, 0, R.string.menu_splitAggregate)
308 .setIcon(android.R.drawable.ic_menu_share);
Dmitri Plotnikov49f705f2009-06-17 18:31:56 -0700309 menu.add(0, MENU_ITEM_JOIN_AGGREGATE, 0, R.string.menu_joinAggregate)
310 .setIcon(android.R.drawable.ic_menu_add);
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800311 return true;
312 }
313
314 @Override
315 public boolean onPrepareOptionsMenu(Menu menu) {
316 super.onPrepareOptionsMenu(menu);
317 // Perform this check each time the menu is about to be shown, because the Barcode Scanner
318 // could be installed or uninstalled at any time.
319 if (isBarcodeScannerInstalled()) {
320 if (menu.findItem(MENU_ITEM_SHOW_BARCODE) == null) {
321 menu.add(0, MENU_ITEM_SHOW_BARCODE, 0, R.string.menu_showBarcode)
322 .setIcon(R.drawable.ic_menu_show_barcode);
323 }
324 } else {
325 menu.removeItem(MENU_ITEM_SHOW_BARCODE);
326 }
Dmitri Plotnikovb4491ee2009-06-15 09:31:02 -0700327
328 boolean isAggregate = mContactIds.size() > 1;
329 menu.findItem(MENU_ITEM_SPLIT_AGGREGATE).setEnabled(isAggregate);
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800330 return true;
331 }
332
333 private boolean isBarcodeScannerInstalled() {
334 final Intent intent = new Intent(SHOW_BARCODE_INTENT);
335 ResolveInfo ri = getPackageManager().resolveActivity(intent,
336 PackageManager.MATCH_DEFAULT_ONLY);
337 return ri != null;
338 }
339
340 @Override
341 public void onCreateContextMenu(ContextMenu menu, View view, ContextMenuInfo menuInfo) {
342 AdapterView.AdapterContextMenuInfo info;
343 try {
344 info = (AdapterView.AdapterContextMenuInfo) menuInfo;
345 } catch (ClassCastException e) {
346 Log.e(TAG, "bad menuInfo", e);
347 return;
348 }
349
350 // This can be null sometimes, don't crash...
351 if (info == null) {
352 Log.e(TAG, "bad menuInfo");
353 return;
354 }
Evan Millar5c22c3b2009-05-29 11:37:54 -0700355
Evan Millar45e0ed32009-06-01 16:44:38 -0700356 ViewEntry entry = ContactEntryAdapter.getEntry(mSections, info.position, SHOW_SEPARATORS);
357 if (entry.mimetype.equals(CommonDataKinds.Phone.CONTENT_ITEM_TYPE)) {
358 menu.add(0, 0, 0, R.string.menu_call).setIntent(entry.intent);
359 menu.add(0, 0, 0, R.string.menu_sendSMS).setIntent(entry.auxIntent);
360 if (entry.primaryIcon == -1) {
361 menu.add(0, MENU_ITEM_MAKE_DEFAULT, 0, R.string.menu_makeDefaultNumber);
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800362 }
Evan Millar45e0ed32009-06-01 16:44:38 -0700363 } else if (entry.mimetype.equals(CommonDataKinds.Email.CONTENT_ITEM_TYPE)) {
364 menu.add(0, 0, 0, R.string.menu_sendEmail).setIntent(entry.intent);
365 if (entry.primaryIcon == -1) {
366 menu.add(0, MENU_ITEM_MAKE_DEFAULT, 0, R.string.menu_makeDefaultEmail);
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800367 }
Evan Millar45e0ed32009-06-01 16:44:38 -0700368 } else if (entry.mimetype.equals(CommonDataKinds.Postal.CONTENT_ITEM_TYPE)) {
369 menu.add(0, 0, 0, R.string.menu_viewAddress).setIntent(entry.intent);
370 }
371 // TODO(emillar): add back with group support.
372 /* else if (entry.mimetype.equals()) {
373 menu.add(0, 0, 0, R.string.menu_viewGroup).setIntent(entry.intent);
374 } */
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800375 }
376
377 @Override
378 public boolean onOptionsItemSelected(MenuItem item) {
379 switch (item.getItemId()) {
380 case MENU_ITEM_DELETE: {
381 // Get confirmation
382 showDialog(DIALOG_CONFIRM_DELETE);
383 return true;
384 }
Evan Millar5c22c3b2009-05-29 11:37:54 -0700385
Dmitri Plotnikovb4491ee2009-06-15 09:31:02 -0700386 case MENU_ITEM_SPLIT_AGGREGATE: {
387 showSplitAggregateDialog();
388 return true;
389 }
390
Dmitri Plotnikov49f705f2009-06-17 18:31:56 -0700391 case MENU_ITEM_JOIN_AGGREGATE: {
392 showJoinAggregateActivity();
393 return true;
394 }
395
Evan Millar66388be2009-05-28 15:41:07 -0700396 // TODO(emillar) Bring this back.
397 /*case MENU_ITEM_SHOW_BARCODE:
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800398 if (mCursor.moveToFirst()) {
399 Intent intent = new Intent(SHOW_BARCODE_INTENT);
400 intent.putExtra("ENCODE_TYPE", "CONTACT_TYPE");
401 Bundle bundle = new Bundle();
Evan Millar66388be2009-05-28 15:41:07 -0700402 String name = mCursor.getString(AGGREGATE_DISPLAY_NAME_COLUMN);
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800403 if (!TextUtils.isEmpty(name)) {
Jeffrey Sharkey715b32a2009-03-27 18:56:05 -0700404 // Correctly handle when section headers are hidden
405 int sepAdjust = SHOW_SEPARATORS ? 1 : 0;
Evan Millar5c22c3b2009-05-29 11:37:54 -0700406
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800407 bundle.putString(Contacts.Intents.Insert.NAME, name);
408 // The 0th ViewEntry in each ArrayList below is a separator item
Jeffrey Sharkey715b32a2009-03-27 18:56:05 -0700409 int entriesToAdd = Math.min(mPhoneEntries.size() - sepAdjust, PHONE_KEYS.length);
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800410 for (int x = 0; x < entriesToAdd; x++) {
Jeffrey Sharkey715b32a2009-03-27 18:56:05 -0700411 ViewEntry entry = mPhoneEntries.get(x + sepAdjust);
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800412 bundle.putString(PHONE_KEYS[x], entry.data);
413 }
Jeffrey Sharkey715b32a2009-03-27 18:56:05 -0700414 entriesToAdd = Math.min(mEmailEntries.size() - sepAdjust, EMAIL_KEYS.length);
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800415 for (int x = 0; x < entriesToAdd; x++) {
Jeffrey Sharkey715b32a2009-03-27 18:56:05 -0700416 ViewEntry entry = mEmailEntries.get(x + sepAdjust);
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800417 bundle.putString(EMAIL_KEYS[x], entry.data);
418 }
Jeffrey Sharkey715b32a2009-03-27 18:56:05 -0700419 if (mPostalEntries.size() >= 1 + sepAdjust) {
420 ViewEntry entry = mPostalEntries.get(sepAdjust);
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800421 bundle.putString(Contacts.Intents.Insert.POSTAL, entry.data);
422 }
423 intent.putExtra("ENCODE_DATA", bundle);
424 try {
425 startActivity(intent);
426 } catch (ActivityNotFoundException e) {
427 // The check in onPrepareOptionsMenu() should make this impossible, but
428 // for safety I'm catching the exception rather than crashing. Ideally
429 // I'd call Menu.removeItem() here too, but I don't see a way to get
430 // the options menu.
431 Log.e(TAG, "Show barcode menu item was clicked but Barcode Scanner " +
432 "was not installed.");
433 }
434 return true;
435 }
436 }
Evan Millar66388be2009-05-28 15:41:07 -0700437 break; */
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800438 }
439 return super.onOptionsItemSelected(item);
440 }
Evan Millar5c22c3b2009-05-29 11:37:54 -0700441
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800442 @Override
443 public boolean onContextItemSelected(MenuItem item) {
444 switch (item.getItemId()) {
445 case MENU_ITEM_MAKE_DEFAULT: {
Dmitri Plotnikovb4491ee2009-06-15 09:31:02 -0700446 if (makeItemDefault(item)) {
447 return true;
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800448 }
Dmitri Plotnikovb4491ee2009-06-15 09:31:02 -0700449 break;
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800450 }
451 }
Dmitri Plotnikovb4491ee2009-06-15 09:31:02 -0700452
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800453 return super.onContextItemSelected(item);
454 }
455
Dmitri Plotnikovb4491ee2009-06-15 09:31:02 -0700456 private boolean makeItemDefault(MenuItem item) {
457 ViewEntry entry = getViewEntryForMenuItem(item);
458 if (entry == null) {
459 return false;
460 }
461
462 // Update the primary values in the data record.
463 ContentValues values = new ContentValues(2);
464 values.put(Data.IS_PRIMARY, 1);
465 values.put(Data.IS_SUPER_PRIMARY, 1);
466
467 Log.i(TAG, mUri.toString());
468 getContentResolver().update(ContentUris.withAppendedId(Data.CONTENT_URI, entry.id),
469 values, null, null);
470 dataChanged();
471 return true;
472 }
473
474 /**
475 * Shows a dialog that contains a list of all constituent contacts in this aggregate.
476 * The user picks a contact to be split into its own aggregate or clicks Cancel.
477 */
478 private void showSplitAggregateDialog() {
479
480 // Wrap this dialog in a specific theme so that list items have correct text color.
481 final ContextThemeWrapper dialogContext =
482 new ContextThemeWrapper(this, android.R.style.Theme_Light);
483 AlertDialog.Builder builder =
484 new AlertDialog.Builder(dialogContext);
485 builder.setTitle(getString(R.string.splitAggregate_title));
486
487 final SplitAggregateView view = new SplitAggregateView(dialogContext, mUri);
488 builder.setView(view);
489
490 builder.setInverseBackgroundForced(true);
491 builder.setCancelable(true);
492 builder.setNegativeButton(android.R.string.cancel,
493 new OnClickListener() {
494 public void onClick(DialogInterface dialog, int which) {
495 dialog.dismiss();
496 }
497 });
498 final AlertDialog dialog = builder.create();
499
500 view.setOnContactSelectedListener(new OnContactSelectedListener() {
501 public void onContactSelected(long contactId) {
502 dialog.dismiss();
503 splitContact(contactId);
504 }
505 });
506
507 dialog.show();
508 }
509
510 /**
Dmitri Plotnikov49f705f2009-06-17 18:31:56 -0700511 * Shows a list of aggregates that can be joined into the currently viewed aggregate.
Dmitri Plotnikovb4491ee2009-06-15 09:31:02 -0700512 */
Dmitri Plotnikov49f705f2009-06-17 18:31:56 -0700513 public void showJoinAggregateActivity() {
514 Intent intent = new Intent(ContactsListActivity.JOIN_AGGREGATE);
515 intent.putExtra(ContactsListActivity.EXTRA_AGGREGATE_ID, ContentUris.parseId(mUri));
516 startActivityForResult(intent, REQUEST_JOIN_AGGREGATE);
517 }
Dmitri Plotnikovb4491ee2009-06-15 09:31:02 -0700518
Dmitri Plotnikov49f705f2009-06-17 18:31:56 -0700519 @Override
520 protected void onActivityResult(int requestCode, int resultCode, Intent intent) {
521 if (requestCode == REQUEST_JOIN_AGGREGATE && resultCode == RESULT_OK && intent != null) {
522 final long aggregateId = ContentUris.parseId(intent.getData());
523 joinAggregate(aggregateId);
524 }
525 }
526
527 private void splitContact(long contactId) {
528 setAggregationException(contactId, AggregationExceptions.TYPE_KEEP_OUT);
Dmitri Plotnikovd7c4af22009-06-19 18:31:00 -0700529 Toast.makeText(this, R.string.contactsSplitMessage, Toast.LENGTH_SHORT).show();
Dmitri Plotnikov49f705f2009-06-17 18:31:56 -0700530 mAdapter.notifyDataSetChanged();
531 }
532
533 private void joinAggregate(final long aggregateId) {
534 Cursor c = mResolver.query(Contacts.CONTENT_URI, new String[] {Contacts._ID},
535 Contacts.AGGREGATE_ID + "=" + aggregateId, null, null);
536
537 try {
538 while(c.moveToNext()) {
539 long contactId = c.getLong(0);
540 setAggregationException(contactId, AggregationExceptions.TYPE_KEEP_IN);
541 }
542 } finally {
543 c.close();
544 }
545
Dmitri Plotnikovd7c4af22009-06-19 18:31:00 -0700546 Toast.makeText(this, R.string.contactsJoinedMessage, Toast.LENGTH_SHORT).show();
Dmitri Plotnikov49f705f2009-06-17 18:31:56 -0700547 mAdapter.notifyDataSetChanged();
548 }
549
550 /**
551 * Given a contact ID sets an aggregation exception to either join the contact with the
552 * current aggregate or split off.
553 */
554 protected void setAggregationException(long contactId, int exceptionType) {
Dmitri Plotnikovd09f75c2009-06-16 11:59:22 -0700555 ContentValues values = new ContentValues(3);
556 values.put(AggregationExceptions.AGGREGATE_ID, ContentUris.parseId(mUri));
Dmitri Plotnikov49f705f2009-06-17 18:31:56 -0700557 values.put(AggregationExceptions.CONTACT_ID, contactId);
558 values.put(AggregationExceptions.TYPE, exceptionType);
Dmitri Plotnikovd09f75c2009-06-16 11:59:22 -0700559 mResolver.update(AggregationExceptions.CONTENT_URI, values, null, null);
Dmitri Plotnikovb4491ee2009-06-15 09:31:02 -0700560 }
561
562 private ViewEntry getViewEntryForMenuItem(MenuItem item) {
563 AdapterView.AdapterContextMenuInfo info;
564 try {
565 info = (AdapterView.AdapterContextMenuInfo) item.getMenuInfo();
566 } catch (ClassCastException e) {
567 Log.e(TAG, "bad menuInfo", e);
568 return null;
569 }
570
571 return ContactEntryAdapter.getEntry(mSections, info.position, SHOW_SEPARATORS);
572 }
573
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800574 @Override
575 public boolean onKeyDown(int keyCode, KeyEvent event) {
576 switch (keyCode) {
577 case KeyEvent.KEYCODE_CALL: {
578 try {
579 ITelephony phone = ITelephony.Stub.asInterface(
580 ServiceManager.checkService("phone"));
581 if (phone != null && !phone.isIdle()) {
582 // Skip out and let the key be handled at a higher level
583 break;
584 }
585 } catch (RemoteException re) {
586 // Fall through and try to call the contact
587 }
588
589 int index = getListView().getSelectedItemPosition();
590 if (index != -1) {
591 ViewEntry entry = ViewAdapter.getEntry(mSections, index, SHOW_SEPARATORS);
Evan Millar66388be2009-05-28 15:41:07 -0700592 if (entry.intent.getAction() == Intent.ACTION_CALL_PRIVILEGED) {
593 startActivity(entry.intent);
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800594 }
595 } else if (mNumPhoneNumbers != 0) {
596 // There isn't anything selected, call the default number
597 Intent intent = new Intent(Intent.ACTION_CALL_PRIVILEGED, mUri);
598 startActivity(intent);
599 }
600 return true;
601 }
602
603 case KeyEvent.KEYCODE_DEL: {
604 showDialog(DIALOG_CONFIRM_DELETE);
605 return true;
606 }
607 }
608
609 return super.onKeyDown(keyCode, event);
610 }
611
612 @Override
613 protected void onListItemClick(ListView l, View v, int position, long id) {
614 ViewEntry entry = ViewAdapter.getEntry(mSections, position, SHOW_SEPARATORS);
615 if (entry != null) {
616 Intent intent = entry.intent;
617 if (intent != null) {
618 try {
619 startActivity(intent);
620 } catch (ActivityNotFoundException e) {
621 Log.e(TAG, "No activity found for intent: " + intent);
622 signalError();
623 }
624 } else {
625 signalError();
626 }
627 } else {
628 signalError();
629 }
630 }
631
632 /**
633 * Signal an error to the user via a beep, or some other method.
634 */
635 private void signalError() {
636 //TODO: implement this when we have the sonification APIs
637 }
638
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800639 private Uri constructImToUrl(String host, String data) {
Evan Millar45e0ed32009-06-01 16:44:38 -0700640 // 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 -0800641 StringBuilder buf = new StringBuilder("imto://");
642 buf.append(host);
643 buf.append('/');
644 buf.append(data);
645 return Uri.parse(buf.toString());
646 }
647
648 /**
649 * Build up the entries to display on the screen.
Evan Millar5c22c3b2009-05-29 11:37:54 -0700650 *
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800651 * @param personCursor the URI for the contact being displayed
652 */
Evan Millar66388be2009-05-28 15:41:07 -0700653 private final void buildEntries(Cursor aggCursor) {
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800654 // Clear out the old entries
655 final int numSections = mSections.size();
656 for (int i = 0; i < numSections; i++) {
657 mSections.get(i).clear();
658 }
659
Dmitri Plotnikovb4491ee2009-06-15 09:31:02 -0700660 mContactIds.clear();
661
Evan Millar66388be2009-05-28 15:41:07 -0700662 // Build up method entries
663 if (mUri != null) {
Evan Millar45e0ed32009-06-01 16:44:38 -0700664 Bitmap photoBitmap = null;
Evan Millar66388be2009-05-28 15:41:07 -0700665 while (aggCursor.moveToNext()) {
666 final String mimetype = aggCursor.getString(DATA_MIMETYPE_COLUMN);
Evan Millar5c22c3b2009-05-29 11:37:54 -0700667
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800668 ViewEntry entry = new ViewEntry();
Evan Millar66388be2009-05-28 15:41:07 -0700669
670 final long id = aggCursor.getLong(DATA_ID_COLUMN);
671 final Uri uri = ContentUris.withAppendedId(Data.CONTENT_URI, id);
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800672 entry.id = id;
673 entry.uri = uri;
Evan Millar45e0ed32009-06-01 16:44:38 -0700674 entry.mimetype = mimetype;
Dmitri Plotnikovb4491ee2009-06-15 09:31:02 -0700675 entry.contactId = aggCursor.getLong(DATA_CONTACT_ID_COLUMN);
676 if (!mContactIds.contains(entry.contactId)) {
677 mContactIds.add(entry.contactId);
678 }
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800679
Evan Millar66388be2009-05-28 15:41:07 -0700680 if (mimetype.equals(CommonDataKinds.Phone.CONTENT_ITEM_TYPE)
Evan Millar5c22c3b2009-05-29 11:37:54 -0700681 || mimetype.equals(CommonDataKinds.Email.CONTENT_ITEM_TYPE)
682 || mimetype.equals(CommonDataKinds.Postal.CONTENT_ITEM_TYPE)
Evan Millar66388be2009-05-28 15:41:07 -0700683 || mimetype.equals(CommonDataKinds.Im.CONTENT_ITEM_TYPE)) {
684 final int type = aggCursor.getInt(DATA_1_COLUMN);
Evan Millar45e0ed32009-06-01 16:44:38 -0700685 final String label = aggCursor.getString(DATA_3_COLUMN);
686 final String data = aggCursor.getString(DATA_2_COLUMN);
687 final boolean isSuperPrimary = "1".equals(
688 aggCursor.getString(DATA_IS_SUPER_PRIMARY_COLUMN));
Evan Millar66388be2009-05-28 15:41:07 -0700689
690 // Don't crash if the data is bogus
691 if (TextUtils.isEmpty(data)) {
692 Log.w(TAG, "empty data for contact method " + id);
693 continue;
694 }
695
696 // Build phone entries
697 if (mimetype.equals(CommonDataKinds.Phone.CONTENT_ITEM_TYPE)) {
698 mNumPhoneNumbers++;
Evan Millar5c22c3b2009-05-29 11:37:54 -0700699
Evan Millar66388be2009-05-28 15:41:07 -0700700 final CharSequence displayLabel = ContactsUtils.getDisplayLabel(
701 this, mimetype, type, label);
702 entry.label = buildActionString(R.string.actionCall, displayLabel, true);
703 entry.data = data;
704 entry.intent = new Intent(Intent.ACTION_CALL_PRIVILEGED, entry.uri);
705 entry.auxIntent = new Intent(Intent.ACTION_SENDTO,
706 Uri.fromParts("sms", data, null));
Evan Millar45e0ed32009-06-01 16:44:38 -0700707 if (isSuperPrimary) {
Evan Millar66388be2009-05-28 15:41:07 -0700708 entry.primaryIcon = R.drawable.ic_default_number;
709 }
710 entry.actionIcon = android.R.drawable.sym_action_call;
711 mPhoneEntries.add(entry);
712
Evan Millar5c22c3b2009-05-29 11:37:54 -0700713 if (type == CommonDataKinds.Phone.TYPE_MOBILE
Evan Millar66388be2009-05-28 15:41:07 -0700714 || mShowSmsLinksForAllPhones) {
715 // Add an SMS entry
716 ViewEntry smsEntry = new ViewEntry();
717 smsEntry.label = buildActionString(
718 R.string.actionText, displayLabel, true);
719 smsEntry.data = data;
720 smsEntry.id = id;
721 smsEntry.uri = uri;
722 smsEntry.intent = entry.auxIntent;
Evan Millar45e0ed32009-06-01 16:44:38 -0700723 smsEntry.mimetype = FastTrackWindow.MIME_SMS_ADDRESS;
Evan Millar66388be2009-05-28 15:41:07 -0700724 smsEntry.actionIcon = R.drawable.sym_action_sms;
725 mSmsEntries.add(smsEntry);
726 }
727 // Build email entries
728 } else if (mimetype.equals(CommonDataKinds.Email.CONTENT_ITEM_TYPE)) {
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800729 entry.label = buildActionString(R.string.actionEmail,
Evan Millar66388be2009-05-28 15:41:07 -0700730 ContactsUtils.getDisplayLabel(this, mimetype, type, label), true);
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800731 entry.data = data;
732 entry.intent = new Intent(Intent.ACTION_SENDTO,
733 Uri.fromParts("mailto", data, null));
734 entry.actionIcon = android.R.drawable.sym_action_email;
Evan Millar45e0ed32009-06-01 16:44:38 -0700735 if (isSuperPrimary) {
Evan Millar66388be2009-05-28 15:41:07 -0700736 entry.primaryIcon = R.drawable.ic_default_number;
737 }
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800738 mEmailEntries.add(entry);
Evan Millar66388be2009-05-28 15:41:07 -0700739 // Build postal entries
740 } else if (mimetype.equals(CommonDataKinds.Postal.CONTENT_ITEM_TYPE)) {
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800741 entry.label = buildActionString(R.string.actionMap,
Evan Millar66388be2009-05-28 15:41:07 -0700742 ContactsUtils.getDisplayLabel(this, mimetype, type, label), true);
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800743 entry.data = data;
744 entry.maxLines = 4;
745 entry.intent = new Intent(Intent.ACTION_VIEW, uri);
746 entry.actionIcon = R.drawable.sym_action_map;
747 mPostalEntries.add(entry);
Evan Millar66388be2009-05-28 15:41:07 -0700748 // Build im entries
749 } else if (mimetype.equals(CommonDataKinds.Im.CONTENT_ITEM_TYPE)) {
750 String[] protocolStrings = getResources().getStringArray(
751 android.R.array.imProtocols);
752 Object protocolObj = ContactsUtils.decodeImProtocol(
753 aggCursor.getString(DATA_5_COLUMN));
Alex Kennberg87fc3172009-03-28 06:43:06 -0700754 String host = null;
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800755 if (protocolObj instanceof Number) {
756 int protocol = ((Number) protocolObj).intValue();
757 entry.label = buildActionString(R.string.actionChat,
758 protocolStrings[protocol], false);
Evan Millar66388be2009-05-28 15:41:07 -0700759 host = ContactsUtils.lookupProviderNameFromId(
760 protocol).toLowerCase();
761 if (protocol == CommonDataKinds.Im.PROTOCOL_GOOGLE_TALK
762 || protocol == CommonDataKinds.Im.PROTOCOL_MSN) {
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800763 entry.maxLabelLines = 2;
764 }
Alex Kennberg87fc3172009-03-28 06:43:06 -0700765 } else if (protocolObj != null) {
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800766 String providerName = (String) protocolObj;
767 entry.label = buildActionString(R.string.actionChat,
768 providerName, false);
769 host = providerName.toLowerCase();
770 }
771
772 // Only add the intent if there is a valid host
773 if (!TextUtils.isEmpty(host)) {
774 entry.intent = new Intent(Intent.ACTION_SENDTO,
775 constructImToUrl(host, data));
776 }
777 entry.data = data;
Evan Millar66388be2009-05-28 15:41:07 -0700778 //TODO(emillar) Do we want presence info?
779 /*if (!aggCursor.isNull(METHODS_STATUS_COLUMN)) {
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800780 entry.presenceIcon = Presence.getPresenceIconResourceId(
Evan Millar66388be2009-05-28 15:41:07 -0700781 aggCursor.getInt(METHODS_STATUS_COLUMN));
782 }*/
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800783 entry.actionIcon = android.R.drawable.sym_action_chat;
784 mImEntries.add(entry);
Evan Millar5c22c3b2009-05-29 11:37:54 -0700785 }
Evan Millar7e4accf2009-06-08 10:43:26 -0700786 // Set the name
787 } else if (mimetype.equals(CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE)) {
788 String name = mCursor.getString(DATA_9_COLUMN);
789 if (TextUtils.isEmpty(name)) {
790 mNameView.setText(getText(android.R.string.unknownName));
791 } else {
792 mNameView.setText(name);
793 }
794 /*
795 if (mPhoneticNameView != null) {
796 String phoneticName = mCursor.getString(CONTACT_PHONETIC_NAME_COLUMN);
797 mPhoneticNameView.setText(phoneticName);
798 }
799 */
Evan Millar66388be2009-05-28 15:41:07 -0700800 // Build organization entries
801 } else if (mimetype.equals(CommonDataKinds.Organization.CONTENT_ITEM_TYPE)) {
Evan Millar66388be2009-05-28 15:41:07 -0700802 final String company = aggCursor.getString(DATA_3_COLUMN);
803 final String title = aggCursor.getString(DATA_4_COLUMN);
804 final boolean isPrimary = "1".equals(aggCursor.getString(DATA_5_COLUMN));
805
806 if (isPrimary) {
807 entry.primaryIcon = R.drawable.ic_default_number;
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800808 }
Evan Millar5c22c3b2009-05-29 11:37:54 -0700809
Evan Millar66388be2009-05-28 15:41:07 -0700810 // Don't crash if the data is bogus
811 if (TextUtils.isEmpty(company) && TextUtils.isEmpty(title)) {
812 Log.w(TAG, "empty data for contact method " + id);
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800813 continue;
814 }
Evan Millar5c22c3b2009-05-29 11:37:54 -0700815
Evan Millar66388be2009-05-28 15:41:07 -0700816 entry.data = title;
817 entry.actionIcon = R.drawable.sym_action_organization;
818 entry.label = company;
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800819
Evan Millar66388be2009-05-28 15:41:07 -0700820 mOrganizationEntries.add(entry);
821 // Build note entries
822 } else if (mimetype.equals(CommonDataKinds.Note.CONTENT_ITEM_TYPE)) {
823 entry.label = getString(R.string.label_notes);
824 entry.data = aggCursor.getString(DATA_1_COLUMN);
825 entry.id = 0;
826 entry.uri = null;
827 entry.intent = null;
828 entry.maxLines = 10;
829 entry.actionIcon = R.drawable.sym_note;
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800830
Evan Millar66388be2009-05-28 15:41:07 -0700831 if (TextUtils.isEmpty(entry.data)) {
832 Log.w(TAG, "empty data for contact method " + id);
Alex Kennberg87fc3172009-03-28 06:43:06 -0700833 continue;
834 }
Evan Millar5c22c3b2009-05-29 11:37:54 -0700835
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800836 mOtherEntries.add(entry);
Evan Millar66388be2009-05-28 15:41:07 -0700837 // Build the ringtone and send to voicemail entries
838 } else if (mimetype.equals(CommonDataKinds.CustomRingtone.CONTENT_ITEM_TYPE)) {
839 final Boolean sendToVoicemail = "1".equals(aggCursor.getString(DATA_1_COLUMN));
840 final String ringtoneStr = aggCursor.getString(DATA_2_COLUMN);
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800841
Evan Millar5c22c3b2009-05-29 11:37:54 -0700842 // TODO(emillar) we need to enforce uniqueness of custom ringtone entries on a
Evan Millar66388be2009-05-28 15:41:07 -0700843 // single aggregate. This could be done by checking against the reference ID stored
844 // in the aggregate.
Evan Millar5c22c3b2009-05-29 11:37:54 -0700845
Evan Millar66388be2009-05-28 15:41:07 -0700846 // Build the send directly to voice mail entry
847 if (sendToVoicemail) {
848 entry.label = getString(R.string.actionIncomingCall);
849 entry.data = getString(R.string.detailIncomingCallsGoToVoicemail);
850 entry.actionIcon = R.drawable.sym_send_to_voicemail;
851 mOtherEntries.add(entry);
852 } else if (!TextUtils.isEmpty(ringtoneStr)) {
853 // Get the URI
854 Uri ringtoneUri = Uri.parse(ringtoneStr);
855 if (ringtoneUri != null) {
856 Ringtone ringtone = RingtoneManager.getRingtone(this, ringtoneUri);
857 if (ringtone != null) {
858 entry.label = getString(R.string.label_ringtone);
859 entry.data = ringtone.getTitle(this);
860 entry.uri = ringtoneUri;
861 entry.actionIcon = R.drawable.sym_ringtone;
862 mOtherEntries.add(entry);
863 }
864 }
865 }
Evan Millar45e0ed32009-06-01 16:44:38 -0700866 // Load the photo
867 } else if (mimetype.equals(CommonDataKinds.Photo.CONTENT_ITEM_TYPE)) {
868 photoBitmap = ContactsUtils.loadContactPhoto(aggCursor, DATA_1_COLUMN, null);
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 }
Evan Millar45e0ed32009-06-01 16:44:38 -0700916
917 if (photoBitmap == null) {
918 photoBitmap = ContactsUtils.loadPlaceholderPhoto(mNoPhotoResource, this, null);
919 }
920 mPhotoView.setImageBitmap(photoBitmap);
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800921 }
922 }
923
Evan Millar66388be2009-05-28 15:41:07 -0700924
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800925 String buildActionString(int actionResId, CharSequence type, boolean lowerCase) {
Jeff Hamilton8350e5b2009-03-24 21:01:34 -0700926 // If there is no type just display an empty string
927 if (type == null) {
928 type = "";
929 }
930
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800931 if (lowerCase) {
932 return getString(actionResId, type.toString().toLowerCase());
933 } else {
934 return getString(actionResId, type.toString());
935 }
936 }
Evan Millar5c22c3b2009-05-29 11:37:54 -0700937
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800938 /**
939 * A basic structure with the data for a contact entry in the list.
940 */
941 final static class ViewEntry extends ContactEntryAdapter.Entry {
942 public int primaryIcon = -1;
943 public Intent intent;
944 public Intent auxIntent = null;
945 public int presenceIcon = -1;
946 public int actionIcon = -1;
947 public int maxLabelLines = 1;
948 }
949
950 private static final class ViewAdapter extends ContactEntryAdapter<ViewEntry> {
951 /** Cache of the children views of a row */
952 static class ViewCache {
953 public TextView label;
954 public TextView data;
955 public ImageView actionIcon;
956 public ImageView presenceIcon;
Evan Millar5c22c3b2009-05-29 11:37:54 -0700957
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800958 // Need to keep track of this too
959 ViewEntry entry;
960 }
Evan Millar5c22c3b2009-05-29 11:37:54 -0700961
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800962 ViewAdapter(Context context, ArrayList<ArrayList<ViewEntry>> sections) {
963 super(context, sections, SHOW_SEPARATORS);
964 }
965
966 @Override
967 public View getView(int position, View convertView, ViewGroup parent) {
Evan Millar5c22c3b2009-05-29 11:37:54 -0700968 ViewEntry entry = getEntry(mSections, position, false);
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800969 View v;
970
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800971 ViewCache views;
972
973 // Check to see if we can reuse convertView
974 if (convertView != null) {
975 v = convertView;
976 views = (ViewCache) v.getTag();
977 } else {
978 // Create a new view if needed
979 v = mInflater.inflate(R.layout.list_item_text_icons, parent, false);
980
981 // Cache the children
982 views = new ViewCache();
983 views.label = (TextView) v.findViewById(android.R.id.text1);
984 views.data = (TextView) v.findViewById(android.R.id.text2);
985 views.actionIcon = (ImageView) v.findViewById(R.id.icon1);
986 views.presenceIcon = (ImageView) v.findViewById(R.id.icon2);
987 v.setTag(views);
988 }
989
990 // Update the entry in the view cache
991 views.entry = entry;
992
993 // Bind the data to the view
994 bindView(v, entry);
995 return v;
996 }
997
998 @Override
999 protected View newView(int position, ViewGroup parent) {
1000 // getView() handles this
1001 throw new UnsupportedOperationException();
1002 }
1003
1004 @Override
1005 protected void bindView(View view, ViewEntry entry) {
1006 final Resources resources = mContext.getResources();
1007 ViewCache views = (ViewCache) view.getTag();
1008
1009 // Set the label
1010 TextView label = views.label;
1011 setMaxLines(label, entry.maxLabelLines);
1012 label.setText(entry.label);
1013
1014 // Set the data
1015 TextView data = views.data;
1016 if (data != null) {
1017 data.setText(entry.data);
1018 setMaxLines(data, entry.maxLines);
1019 }
1020
1021 // Set the action icon
1022 ImageView action = views.actionIcon;
1023 if (entry.actionIcon != -1) {
1024 action.setImageDrawable(resources.getDrawable(entry.actionIcon));
1025 action.setVisibility(View.VISIBLE);
1026 } else {
1027 // Things should still line up as if there was an icon, so make it invisible
1028 action.setVisibility(View.INVISIBLE);
1029 }
1030
1031 // Set the presence icon
1032 Drawable presenceIcon = null;
1033 if (entry.primaryIcon != -1) {
1034 presenceIcon = resources.getDrawable(entry.primaryIcon);
1035 } else if (entry.presenceIcon != -1) {
1036 presenceIcon = resources.getDrawable(entry.presenceIcon);
1037 }
1038
1039 ImageView presence = views.presenceIcon;
1040 if (presenceIcon != null) {
1041 presence.setImageDrawable(presenceIcon);
1042 presence.setVisibility(View.VISIBLE);
1043 } else {
1044 presence.setVisibility(View.GONE);
1045 }
1046 }
1047
1048 private void setMaxLines(TextView textView, int maxLines) {
1049 if (maxLines == 1) {
1050 textView.setSingleLine(true);
1051 textView.setEllipsize(TextUtils.TruncateAt.END);
1052 } else {
1053 textView.setSingleLine(false);
1054 textView.setMaxLines(maxLines);
1055 textView.setEllipsize(null);
1056 }
1057 }
1058 }
1059}