blob: ae5c252b078302c12a895a0f8d72f67a17dfa6a1 [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;
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -080053import android.net.Uri;
54import android.os.Bundle;
55import android.os.Handler;
56import android.os.RemoteException;
57import android.os.ServiceManager;
58import android.os.SystemClock;
Dmitri Plotnikov49f705f2009-06-17 18:31:56 -070059import android.provider.ContactsContract.Contacts;
Evan Millar66388be2009-05-28 15:41:07 -070060import android.provider.ContactsContract.Aggregates;
Dmitri Plotnikovb4491ee2009-06-15 09:31:02 -070061import android.provider.ContactsContract.AggregationExceptions;
62import android.provider.ContactsContract.CommonDataKinds;
Evan Millar66388be2009-05-28 15:41:07 -070063import android.provider.ContactsContract.Data;
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -080064import android.text.TextUtils;
65import android.util.Log;
66import android.view.ContextMenu;
Dmitri Plotnikovb4491ee2009-06-15 09:31:02 -070067import android.view.ContextThemeWrapper;
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -080068import android.view.KeyEvent;
69import android.view.Menu;
70import android.view.MenuItem;
71import android.view.View;
72import android.view.ViewGroup;
73import android.view.ContextMenu.ContextMenuInfo;
74import android.widget.AdapterView;
75import android.widget.CheckBox;
76import android.widget.ImageView;
77import android.widget.ListView;
78import android.widget.TextView;
79import android.widget.Toast;
80
81import java.util.ArrayList;
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -080082
83/**
84 * Displays the details of a specific contact.
85 */
Evan Millar5c22c3b2009-05-29 11:37:54 -070086public class ViewContactActivity extends ListActivity
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -080087 implements View.OnCreateContextMenuListener, View.OnClickListener,
88 DialogInterface.OnClickListener {
89 private static final String TAG = "ViewContact";
90 private static final String SHOW_BARCODE_INTENT = "com.google.zxing.client.android.ENCODE";
91
92 private static final boolean SHOW_SEPARATORS = false;
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -080093
94 private static final int DIALOG_CONFIRM_DELETE = 1;
95
Dmitri Plotnikov49f705f2009-06-17 18:31:56 -070096 private static final int REQUEST_JOIN_AGGREGATE = 1;
97
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -080098 public static final int MENU_ITEM_DELETE = 1;
99 public static final int MENU_ITEM_MAKE_DEFAULT = 2;
100 public static final int MENU_ITEM_SHOW_BARCODE = 3;
Dmitri Plotnikovb4491ee2009-06-15 09:31:02 -0700101 public static final int MENU_ITEM_SPLIT_AGGREGATE = 4;
Dmitri Plotnikov49f705f2009-06-17 18:31:56 -0700102 public static final int MENU_ITEM_JOIN_AGGREGATE = 5;
Dmitri Plotnikovef038722009-06-24 18:51:47 -0700103 public static final int MENU_ITEM_OPTIONS = 6;
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800104
105 private Uri mUri;
Evan Millar45e0ed32009-06-01 16:44:38 -0700106 private Uri mAggDataUri;
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800107 private ContentResolver mResolver;
108 private ViewAdapter mAdapter;
109 private int mNumPhoneNumbers = 0;
110
Dmitri Plotnikovb4491ee2009-06-15 09:31:02 -0700111 /**
112 * A list of distinct contact IDs included in the current aggregate.
113 */
114 private ArrayList<Long> mContactIds = new ArrayList<Long>();
115
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800116 /* package */ ArrayList<ViewEntry> mPhoneEntries = new ArrayList<ViewEntry>();
117 /* package */ ArrayList<ViewEntry> mSmsEntries = new ArrayList<ViewEntry>();
118 /* package */ ArrayList<ViewEntry> mEmailEntries = new ArrayList<ViewEntry>();
119 /* package */ ArrayList<ViewEntry> mPostalEntries = new ArrayList<ViewEntry>();
120 /* package */ ArrayList<ViewEntry> mImEntries = new ArrayList<ViewEntry>();
121 /* package */ ArrayList<ViewEntry> mOrganizationEntries = new ArrayList<ViewEntry>();
The Android Open Source Projectcac191e2009-03-18 22:20:27 -0700122 /* package */ ArrayList<ViewEntry> mGroupEntries = new ArrayList<ViewEntry>();
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800123 /* package */ ArrayList<ViewEntry> mOtherEntries = new ArrayList<ViewEntry>();
124 /* package */ ArrayList<ArrayList<ViewEntry>> mSections = new ArrayList<ArrayList<ViewEntry>>();
125
126 private Cursor mCursor;
127 private boolean mObserverRegistered;
Evan Millar5c22c3b2009-05-29 11:37:54 -0700128
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800129 private ContentObserver mObserver = new ContentObserver(new Handler()) {
130 @Override
131 public boolean deliverSelfNotifications() {
132 return true;
133 }
134
135 @Override
136 public void onChange(boolean selfChange) {
137 if (mCursor != null && !mCursor.isClosed()){
138 dataChanged();
139 }
140 }
141 };
142
143 public void onClick(DialogInterface dialog, int which) {
144 if (mCursor != null) {
145 if (mObserverRegistered) {
146 mCursor.unregisterContentObserver(mObserver);
147 mObserverRegistered = false;
148 }
149 mCursor.close();
150 mCursor = null;
151 }
152 getContentResolver().delete(mUri, null, null);
153 finish();
154 }
155
156 public void onClick(View view) {
157 if (!mObserverRegistered) {
158 return;
159 }
160 switch (view.getId()) {
161 case R.id.star: {
Evan Millar7e4accf2009-06-08 10:43:26 -0700162 mCursor.moveToFirst();
Evan Millar66388be2009-05-28 15:41:07 -0700163 int oldStarredState = mCursor.getInt(AGGREGATE_STARRED_COLUMN);
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800164 ContentValues values = new ContentValues(1);
Evan Millar66388be2009-05-28 15:41:07 -0700165 values.put(Aggregates.STARRED, oldStarredState == 1 ? 0 : 1);
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800166 getContentResolver().update(mUri, values, null, null);
167 break;
168 }
169 }
170 }
171
172 private TextView mNameView;
173 private TextView mPhoneticNameView; // may be null in some locales
174 private ImageView mPhotoView;
175 private int mNoPhotoResource;
176 private CheckBox mStarView;
177 private boolean mShowSmsLinksForAllPhones;
178
179 @Override
180 protected void onCreate(Bundle icicle) {
181 super.onCreate(icicle);
182
183 setContentView(R.layout.view_contact);
184 getListView().setOnCreateContextMenuListener(this);
185
186 mNameView = (TextView) findViewById(R.id.name);
187 mPhoneticNameView = (TextView) findViewById(R.id.phonetic_name);
188 mPhotoView = (ImageView) findViewById(R.id.photo);
189 mStarView = (CheckBox) findViewById(R.id.star);
190 mStarView.setOnClickListener(this);
191
192 // Set the photo with a random "no contact" image
193 long now = SystemClock.elapsedRealtime();
194 int num = (int) now & 0xf;
195 if (num < 9) {
196 // Leaning in from right, common
197 mNoPhotoResource = R.drawable.ic_contact_picture;
198 } else if (num < 14) {
199 // Leaning in from left uncommon
200 mNoPhotoResource = R.drawable.ic_contact_picture_2;
201 } else {
202 // Coming in from the top, rare
203 mNoPhotoResource = R.drawable.ic_contact_picture_3;
204 }
205
Evan Millar45e0ed32009-06-01 16:44:38 -0700206 mUri = getIntent().getData();
207 mAggDataUri = Uri.withAppendedPath(mUri, "data");
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800208 mResolver = getContentResolver();
209
210 // Build the list of sections. The order they're added to mSections dictates the
211 // order they are displayed in the list.
212 mSections.add(mPhoneEntries);
213 mSections.add(mSmsEntries);
214 mSections.add(mEmailEntries);
215 mSections.add(mImEntries);
216 mSections.add(mPostalEntries);
217 mSections.add(mOrganizationEntries);
The Android Open Source Projectcac191e2009-03-18 22:20:27 -0700218 mSections.add(mGroupEntries);
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800219 mSections.add(mOtherEntries);
220
221 //TODO Read this value from a preference
222 mShowSmsLinksForAllPhones = true;
223
Evan Millar45e0ed32009-06-01 16:44:38 -0700224 mCursor = mResolver.query(mAggDataUri,
225 AGGREGATE_PROJECTION, null, null, null);
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800226 }
227
228 @Override
229 protected void onResume() {
230 super.onResume();
231 mObserverRegistered = true;
232 mCursor.registerContentObserver(mObserver);
233 dataChanged();
234 }
235
236 @Override
237 protected void onPause() {
238 super.onPause();
239 if (mCursor != null) {
240 if (mObserverRegistered) {
241 mObserverRegistered = false;
242 mCursor.unregisterContentObserver(mObserver);
243 }
244 mCursor.deactivate();
245 }
246 }
247
248 @Override
249 protected void onDestroy() {
250 super.onDestroy();
251
252 if (mCursor != null) {
253 if (mObserverRegistered) {
254 mCursor.unregisterContentObserver(mObserver);
255 mObserverRegistered = false;
256 }
257 mCursor.close();
258 }
259 }
260
261 @Override
262 protected Dialog onCreateDialog(int id) {
263 switch (id) {
264 case DIALOG_CONFIRM_DELETE:
265 return new AlertDialog.Builder(this)
266 .setTitle(R.string.deleteConfirmation_title)
267 .setIcon(android.R.drawable.ic_dialog_alert)
268 .setMessage(R.string.deleteConfirmation)
269 .setNegativeButton(android.R.string.cancel, null)
270 .setPositiveButton(android.R.string.ok, this)
271 .setCancelable(false)
272 .create();
273 }
274 return null;
275 }
276
277 private void dataChanged() {
278 mCursor.requery();
279 if (mCursor.moveToFirst()) {
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800280 // Set the star
Evan Millar66388be2009-05-28 15:41:07 -0700281 mStarView.setChecked(mCursor.getInt(AGGREGATE_STARRED_COLUMN) == 1 ? true : false);
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800282
283 // Build up the contact entries
284 buildEntries(mCursor);
285 if (mAdapter == null) {
286 mAdapter = new ViewAdapter(this, mSections);
287 setListAdapter(mAdapter);
288 } else {
289 mAdapter.setSections(mSections, SHOW_SEPARATORS);
290 }
291 } else {
292 Toast.makeText(this, R.string.invalidContactMessage, Toast.LENGTH_SHORT).show();
293 Log.e(TAG, "invalid contact uri: " + mUri);
294 finish();
295 }
296 }
297
298 @Override
299 public boolean onCreateOptionsMenu(Menu menu) {
300 menu.add(0, 0, 0, R.string.menu_editContact)
301 .setIcon(android.R.drawable.ic_menu_edit)
302 .setIntent(new Intent(Intent.ACTION_EDIT, mUri))
303 .setAlphabeticShortcut('e');
304 menu.add(0, MENU_ITEM_DELETE, 0, R.string.menu_deleteContact)
305 .setIcon(android.R.drawable.ic_menu_delete);
Dmitri Plotnikovb4491ee2009-06-15 09:31:02 -0700306 menu.add(0, MENU_ITEM_SPLIT_AGGREGATE, 0, R.string.menu_splitAggregate)
307 .setIcon(android.R.drawable.ic_menu_share);
Dmitri Plotnikov49f705f2009-06-17 18:31:56 -0700308 menu.add(0, MENU_ITEM_JOIN_AGGREGATE, 0, R.string.menu_joinAggregate)
309 .setIcon(android.R.drawable.ic_menu_add);
Dmitri Plotnikovef038722009-06-24 18:51:47 -0700310 menu.add(0, MENU_ITEM_OPTIONS, 0, R.string.menu_contactOptions)
311 .setIcon(R.drawable.ic_menu_mark);
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800312 return true;
313 }
314
315 @Override
316 public boolean onPrepareOptionsMenu(Menu menu) {
317 super.onPrepareOptionsMenu(menu);
318 // Perform this check each time the menu is about to be shown, because the Barcode Scanner
319 // could be installed or uninstalled at any time.
320 if (isBarcodeScannerInstalled()) {
321 if (menu.findItem(MENU_ITEM_SHOW_BARCODE) == null) {
322 menu.add(0, MENU_ITEM_SHOW_BARCODE, 0, R.string.menu_showBarcode)
323 .setIcon(R.drawable.ic_menu_show_barcode);
324 }
325 } else {
326 menu.removeItem(MENU_ITEM_SHOW_BARCODE);
327 }
Dmitri Plotnikovb4491ee2009-06-15 09:31:02 -0700328
329 boolean isAggregate = mContactIds.size() > 1;
330 menu.findItem(MENU_ITEM_SPLIT_AGGREGATE).setEnabled(isAggregate);
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800331 return true;
332 }
333
334 private boolean isBarcodeScannerInstalled() {
335 final Intent intent = new Intent(SHOW_BARCODE_INTENT);
336 ResolveInfo ri = getPackageManager().resolveActivity(intent,
337 PackageManager.MATCH_DEFAULT_ONLY);
338 return ri != null;
339 }
340
341 @Override
342 public void onCreateContextMenu(ContextMenu menu, View view, ContextMenuInfo menuInfo) {
343 AdapterView.AdapterContextMenuInfo info;
344 try {
345 info = (AdapterView.AdapterContextMenuInfo) menuInfo;
346 } catch (ClassCastException e) {
347 Log.e(TAG, "bad menuInfo", e);
348 return;
349 }
350
351 // This can be null sometimes, don't crash...
352 if (info == null) {
353 Log.e(TAG, "bad menuInfo");
354 return;
355 }
Evan Millar5c22c3b2009-05-29 11:37:54 -0700356
Evan Millar45e0ed32009-06-01 16:44:38 -0700357 ViewEntry entry = ContactEntryAdapter.getEntry(mSections, info.position, SHOW_SEPARATORS);
358 if (entry.mimetype.equals(CommonDataKinds.Phone.CONTENT_ITEM_TYPE)) {
359 menu.add(0, 0, 0, R.string.menu_call).setIntent(entry.intent);
360 menu.add(0, 0, 0, R.string.menu_sendSMS).setIntent(entry.auxIntent);
361 if (entry.primaryIcon == -1) {
362 menu.add(0, MENU_ITEM_MAKE_DEFAULT, 0, R.string.menu_makeDefaultNumber);
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800363 }
Evan Millar45e0ed32009-06-01 16:44:38 -0700364 } else if (entry.mimetype.equals(CommonDataKinds.Email.CONTENT_ITEM_TYPE)) {
365 menu.add(0, 0, 0, R.string.menu_sendEmail).setIntent(entry.intent);
366 if (entry.primaryIcon == -1) {
367 menu.add(0, MENU_ITEM_MAKE_DEFAULT, 0, R.string.menu_makeDefaultEmail);
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800368 }
Evan Millar45e0ed32009-06-01 16:44:38 -0700369 } else if (entry.mimetype.equals(CommonDataKinds.Postal.CONTENT_ITEM_TYPE)) {
370 menu.add(0, 0, 0, R.string.menu_viewAddress).setIntent(entry.intent);
371 }
372 // TODO(emillar): add back with group support.
373 /* else if (entry.mimetype.equals()) {
374 menu.add(0, 0, 0, R.string.menu_viewGroup).setIntent(entry.intent);
375 } */
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800376 }
377
378 @Override
379 public boolean onOptionsItemSelected(MenuItem item) {
380 switch (item.getItemId()) {
381 case MENU_ITEM_DELETE: {
382 // Get confirmation
383 showDialog(DIALOG_CONFIRM_DELETE);
384 return true;
385 }
Evan Millar5c22c3b2009-05-29 11:37:54 -0700386
Dmitri Plotnikovb4491ee2009-06-15 09:31:02 -0700387 case MENU_ITEM_SPLIT_AGGREGATE: {
388 showSplitAggregateDialog();
389 return true;
390 }
391
Dmitri Plotnikov49f705f2009-06-17 18:31:56 -0700392 case MENU_ITEM_JOIN_AGGREGATE: {
393 showJoinAggregateActivity();
394 return true;
395 }
396
Dmitri Plotnikovef038722009-06-24 18:51:47 -0700397 case MENU_ITEM_OPTIONS: {
398 showOptionsActivity();
399 return true;
400 }
401
Evan Millar66388be2009-05-28 15:41:07 -0700402 // TODO(emillar) Bring this back.
403 /*case MENU_ITEM_SHOW_BARCODE:
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800404 if (mCursor.moveToFirst()) {
405 Intent intent = new Intent(SHOW_BARCODE_INTENT);
406 intent.putExtra("ENCODE_TYPE", "CONTACT_TYPE");
407 Bundle bundle = new Bundle();
Evan Millar66388be2009-05-28 15:41:07 -0700408 String name = mCursor.getString(AGGREGATE_DISPLAY_NAME_COLUMN);
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800409 if (!TextUtils.isEmpty(name)) {
Jeffrey Sharkey715b32a2009-03-27 18:56:05 -0700410 // Correctly handle when section headers are hidden
411 int sepAdjust = SHOW_SEPARATORS ? 1 : 0;
Evan Millar5c22c3b2009-05-29 11:37:54 -0700412
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800413 bundle.putString(Contacts.Intents.Insert.NAME, name);
414 // The 0th ViewEntry in each ArrayList below is a separator item
Jeffrey Sharkey715b32a2009-03-27 18:56:05 -0700415 int entriesToAdd = Math.min(mPhoneEntries.size() - sepAdjust, PHONE_KEYS.length);
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800416 for (int x = 0; x < entriesToAdd; x++) {
Jeffrey Sharkey715b32a2009-03-27 18:56:05 -0700417 ViewEntry entry = mPhoneEntries.get(x + sepAdjust);
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800418 bundle.putString(PHONE_KEYS[x], entry.data);
419 }
Jeffrey Sharkey715b32a2009-03-27 18:56:05 -0700420 entriesToAdd = Math.min(mEmailEntries.size() - sepAdjust, EMAIL_KEYS.length);
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800421 for (int x = 0; x < entriesToAdd; x++) {
Jeffrey Sharkey715b32a2009-03-27 18:56:05 -0700422 ViewEntry entry = mEmailEntries.get(x + sepAdjust);
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800423 bundle.putString(EMAIL_KEYS[x], entry.data);
424 }
Jeffrey Sharkey715b32a2009-03-27 18:56:05 -0700425 if (mPostalEntries.size() >= 1 + sepAdjust) {
426 ViewEntry entry = mPostalEntries.get(sepAdjust);
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800427 bundle.putString(Contacts.Intents.Insert.POSTAL, entry.data);
428 }
429 intent.putExtra("ENCODE_DATA", bundle);
430 try {
431 startActivity(intent);
432 } catch (ActivityNotFoundException e) {
433 // The check in onPrepareOptionsMenu() should make this impossible, but
434 // for safety I'm catching the exception rather than crashing. Ideally
435 // I'd call Menu.removeItem() here too, but I don't see a way to get
436 // the options menu.
437 Log.e(TAG, "Show barcode menu item was clicked but Barcode Scanner " +
438 "was not installed.");
439 }
440 return true;
441 }
442 }
Evan Millar66388be2009-05-28 15:41:07 -0700443 break; */
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800444 }
445 return super.onOptionsItemSelected(item);
446 }
Evan Millar5c22c3b2009-05-29 11:37:54 -0700447
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800448 @Override
449 public boolean onContextItemSelected(MenuItem item) {
450 switch (item.getItemId()) {
451 case MENU_ITEM_MAKE_DEFAULT: {
Dmitri Plotnikovb4491ee2009-06-15 09:31:02 -0700452 if (makeItemDefault(item)) {
453 return true;
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800454 }
Dmitri Plotnikovb4491ee2009-06-15 09:31:02 -0700455 break;
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800456 }
457 }
Dmitri Plotnikovb4491ee2009-06-15 09:31:02 -0700458
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800459 return super.onContextItemSelected(item);
460 }
461
Dmitri Plotnikovb4491ee2009-06-15 09:31:02 -0700462 private boolean makeItemDefault(MenuItem item) {
463 ViewEntry entry = getViewEntryForMenuItem(item);
464 if (entry == null) {
465 return false;
466 }
467
468 // Update the primary values in the data record.
469 ContentValues values = new ContentValues(2);
470 values.put(Data.IS_PRIMARY, 1);
471 values.put(Data.IS_SUPER_PRIMARY, 1);
472
473 Log.i(TAG, mUri.toString());
474 getContentResolver().update(ContentUris.withAppendedId(Data.CONTENT_URI, entry.id),
475 values, null, null);
476 dataChanged();
477 return true;
478 }
479
480 /**
481 * Shows a dialog that contains a list of all constituent contacts in this aggregate.
482 * The user picks a contact to be split into its own aggregate or clicks Cancel.
483 */
484 private void showSplitAggregateDialog() {
485
486 // Wrap this dialog in a specific theme so that list items have correct text color.
487 final ContextThemeWrapper dialogContext =
488 new ContextThemeWrapper(this, android.R.style.Theme_Light);
489 AlertDialog.Builder builder =
490 new AlertDialog.Builder(dialogContext);
491 builder.setTitle(getString(R.string.splitAggregate_title));
492
493 final SplitAggregateView view = new SplitAggregateView(dialogContext, mUri);
494 builder.setView(view);
495
496 builder.setInverseBackgroundForced(true);
497 builder.setCancelable(true);
498 builder.setNegativeButton(android.R.string.cancel,
499 new OnClickListener() {
500 public void onClick(DialogInterface dialog, int which) {
501 dialog.dismiss();
502 }
503 });
504 final AlertDialog dialog = builder.create();
505
506 view.setOnContactSelectedListener(new OnContactSelectedListener() {
507 public void onContactSelected(long contactId) {
508 dialog.dismiss();
509 splitContact(contactId);
510 }
511 });
512
513 dialog.show();
514 }
515
516 /**
Dmitri Plotnikov49f705f2009-06-17 18:31:56 -0700517 * Shows a list of aggregates that can be joined into the currently viewed aggregate.
Dmitri Plotnikovb4491ee2009-06-15 09:31:02 -0700518 */
Dmitri Plotnikov49f705f2009-06-17 18:31:56 -0700519 public void showJoinAggregateActivity() {
520 Intent intent = new Intent(ContactsListActivity.JOIN_AGGREGATE);
521 intent.putExtra(ContactsListActivity.EXTRA_AGGREGATE_ID, ContentUris.parseId(mUri));
522 startActivityForResult(intent, REQUEST_JOIN_AGGREGATE);
523 }
Dmitri Plotnikovb4491ee2009-06-15 09:31:02 -0700524
Dmitri Plotnikov49f705f2009-06-17 18:31:56 -0700525 @Override
526 protected void onActivityResult(int requestCode, int resultCode, Intent intent) {
527 if (requestCode == REQUEST_JOIN_AGGREGATE && resultCode == RESULT_OK && intent != null) {
528 final long aggregateId = ContentUris.parseId(intent.getData());
529 joinAggregate(aggregateId);
530 }
531 }
532
533 private void splitContact(long contactId) {
534 setAggregationException(contactId, AggregationExceptions.TYPE_KEEP_OUT);
Dmitri Plotnikovd7c4af22009-06-19 18:31:00 -0700535 Toast.makeText(this, R.string.contactsSplitMessage, Toast.LENGTH_SHORT).show();
Dmitri Plotnikov49f705f2009-06-17 18:31:56 -0700536 mAdapter.notifyDataSetChanged();
537 }
538
539 private void joinAggregate(final long aggregateId) {
540 Cursor c = mResolver.query(Contacts.CONTENT_URI, new String[] {Contacts._ID},
541 Contacts.AGGREGATE_ID + "=" + aggregateId, null, null);
542
543 try {
544 while(c.moveToNext()) {
545 long contactId = c.getLong(0);
546 setAggregationException(contactId, AggregationExceptions.TYPE_KEEP_IN);
547 }
548 } finally {
549 c.close();
550 }
551
Dmitri Plotnikovd7c4af22009-06-19 18:31:00 -0700552 Toast.makeText(this, R.string.contactsJoinedMessage, Toast.LENGTH_SHORT).show();
Dmitri Plotnikov49f705f2009-06-17 18:31:56 -0700553 mAdapter.notifyDataSetChanged();
554 }
555
556 /**
557 * Given a contact ID sets an aggregation exception to either join the contact with the
558 * current aggregate or split off.
559 */
560 protected void setAggregationException(long contactId, int exceptionType) {
Dmitri Plotnikovd09f75c2009-06-16 11:59:22 -0700561 ContentValues values = new ContentValues(3);
562 values.put(AggregationExceptions.AGGREGATE_ID, ContentUris.parseId(mUri));
Dmitri Plotnikov49f705f2009-06-17 18:31:56 -0700563 values.put(AggregationExceptions.CONTACT_ID, contactId);
564 values.put(AggregationExceptions.TYPE, exceptionType);
Dmitri Plotnikovd09f75c2009-06-16 11:59:22 -0700565 mResolver.update(AggregationExceptions.CONTENT_URI, values, null, null);
Dmitri Plotnikovb4491ee2009-06-15 09:31:02 -0700566 }
567
Dmitri Plotnikovef038722009-06-24 18:51:47 -0700568 private void showOptionsActivity() {
569 final Intent intent = new Intent(this, ContactOptionsActivity.class);
570 intent.setData(mUri);
571 startActivity(intent);
572 }
573
Dmitri Plotnikovb4491ee2009-06-15 09:31:02 -0700574 private ViewEntry getViewEntryForMenuItem(MenuItem item) {
575 AdapterView.AdapterContextMenuInfo info;
576 try {
577 info = (AdapterView.AdapterContextMenuInfo) item.getMenuInfo();
578 } catch (ClassCastException e) {
579 Log.e(TAG, "bad menuInfo", e);
580 return null;
581 }
582
583 return ContactEntryAdapter.getEntry(mSections, info.position, SHOW_SEPARATORS);
584 }
585
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800586 @Override
587 public boolean onKeyDown(int keyCode, KeyEvent event) {
588 switch (keyCode) {
589 case KeyEvent.KEYCODE_CALL: {
590 try {
591 ITelephony phone = ITelephony.Stub.asInterface(
592 ServiceManager.checkService("phone"));
593 if (phone != null && !phone.isIdle()) {
594 // Skip out and let the key be handled at a higher level
595 break;
596 }
597 } catch (RemoteException re) {
598 // Fall through and try to call the contact
599 }
600
601 int index = getListView().getSelectedItemPosition();
602 if (index != -1) {
603 ViewEntry entry = ViewAdapter.getEntry(mSections, index, SHOW_SEPARATORS);
Evan Millar66388be2009-05-28 15:41:07 -0700604 if (entry.intent.getAction() == Intent.ACTION_CALL_PRIVILEGED) {
605 startActivity(entry.intent);
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800606 }
607 } else if (mNumPhoneNumbers != 0) {
608 // There isn't anything selected, call the default number
609 Intent intent = new Intent(Intent.ACTION_CALL_PRIVILEGED, mUri);
610 startActivity(intent);
611 }
612 return true;
613 }
614
615 case KeyEvent.KEYCODE_DEL: {
616 showDialog(DIALOG_CONFIRM_DELETE);
617 return true;
618 }
619 }
620
621 return super.onKeyDown(keyCode, event);
622 }
623
624 @Override
625 protected void onListItemClick(ListView l, View v, int position, long id) {
626 ViewEntry entry = ViewAdapter.getEntry(mSections, position, SHOW_SEPARATORS);
627 if (entry != null) {
628 Intent intent = entry.intent;
629 if (intent != null) {
630 try {
631 startActivity(intent);
632 } catch (ActivityNotFoundException e) {
633 Log.e(TAG, "No activity found for intent: " + intent);
634 signalError();
635 }
636 } else {
637 signalError();
638 }
639 } else {
640 signalError();
641 }
642 }
643
644 /**
645 * Signal an error to the user via a beep, or some other method.
646 */
647 private void signalError() {
648 //TODO: implement this when we have the sonification APIs
649 }
650
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800651 private Uri constructImToUrl(String host, String data) {
Evan Millar45e0ed32009-06-01 16:44:38 -0700652 // 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 -0800653 StringBuilder buf = new StringBuilder("imto://");
654 buf.append(host);
655 buf.append('/');
656 buf.append(data);
657 return Uri.parse(buf.toString());
658 }
659
660 /**
661 * Build up the entries to display on the screen.
Evan Millar5c22c3b2009-05-29 11:37:54 -0700662 *
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800663 * @param personCursor the URI for the contact being displayed
664 */
Evan Millar66388be2009-05-28 15:41:07 -0700665 private final void buildEntries(Cursor aggCursor) {
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800666 // Clear out the old entries
667 final int numSections = mSections.size();
668 for (int i = 0; i < numSections; i++) {
669 mSections.get(i).clear();
670 }
671
Dmitri Plotnikovb4491ee2009-06-15 09:31:02 -0700672 mContactIds.clear();
673
Evan Millar66388be2009-05-28 15:41:07 -0700674 // Build up method entries
675 if (mUri != null) {
Evan Millar45e0ed32009-06-01 16:44:38 -0700676 Bitmap photoBitmap = null;
Evan Millar66388be2009-05-28 15:41:07 -0700677 while (aggCursor.moveToNext()) {
678 final String mimetype = aggCursor.getString(DATA_MIMETYPE_COLUMN);
Evan Millar5c22c3b2009-05-29 11:37:54 -0700679
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800680 ViewEntry entry = new ViewEntry();
Evan Millar66388be2009-05-28 15:41:07 -0700681
682 final long id = aggCursor.getLong(DATA_ID_COLUMN);
683 final Uri uri = ContentUris.withAppendedId(Data.CONTENT_URI, id);
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800684 entry.id = id;
685 entry.uri = uri;
Evan Millar45e0ed32009-06-01 16:44:38 -0700686 entry.mimetype = mimetype;
Dmitri Plotnikovb4491ee2009-06-15 09:31:02 -0700687 entry.contactId = aggCursor.getLong(DATA_CONTACT_ID_COLUMN);
688 if (!mContactIds.contains(entry.contactId)) {
689 mContactIds.add(entry.contactId);
690 }
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800691
Evan Millar66388be2009-05-28 15:41:07 -0700692 if (mimetype.equals(CommonDataKinds.Phone.CONTENT_ITEM_TYPE)
Evan Millar5c22c3b2009-05-29 11:37:54 -0700693 || mimetype.equals(CommonDataKinds.Email.CONTENT_ITEM_TYPE)
694 || mimetype.equals(CommonDataKinds.Postal.CONTENT_ITEM_TYPE)
Evan Millar66388be2009-05-28 15:41:07 -0700695 || mimetype.equals(CommonDataKinds.Im.CONTENT_ITEM_TYPE)) {
696 final int type = aggCursor.getInt(DATA_1_COLUMN);
Evan Millar45e0ed32009-06-01 16:44:38 -0700697 final String label = aggCursor.getString(DATA_3_COLUMN);
698 final String data = aggCursor.getString(DATA_2_COLUMN);
699 final boolean isSuperPrimary = "1".equals(
700 aggCursor.getString(DATA_IS_SUPER_PRIMARY_COLUMN));
Evan Millar66388be2009-05-28 15:41:07 -0700701
702 // Don't crash if the data is bogus
703 if (TextUtils.isEmpty(data)) {
704 Log.w(TAG, "empty data for contact method " + id);
705 continue;
706 }
707
708 // Build phone entries
709 if (mimetype.equals(CommonDataKinds.Phone.CONTENT_ITEM_TYPE)) {
710 mNumPhoneNumbers++;
Evan Millar5c22c3b2009-05-29 11:37:54 -0700711
Evan Millar66388be2009-05-28 15:41:07 -0700712 final CharSequence displayLabel = ContactsUtils.getDisplayLabel(
713 this, mimetype, type, label);
714 entry.label = buildActionString(R.string.actionCall, displayLabel, true);
715 entry.data = data;
716 entry.intent = new Intent(Intent.ACTION_CALL_PRIVILEGED, entry.uri);
717 entry.auxIntent = new Intent(Intent.ACTION_SENDTO,
718 Uri.fromParts("sms", data, null));
Evan Millar45e0ed32009-06-01 16:44:38 -0700719 if (isSuperPrimary) {
Evan Millar66388be2009-05-28 15:41:07 -0700720 entry.primaryIcon = R.drawable.ic_default_number;
721 }
722 entry.actionIcon = android.R.drawable.sym_action_call;
723 mPhoneEntries.add(entry);
724
Evan Millar5c22c3b2009-05-29 11:37:54 -0700725 if (type == CommonDataKinds.Phone.TYPE_MOBILE
Evan Millar66388be2009-05-28 15:41:07 -0700726 || mShowSmsLinksForAllPhones) {
727 // Add an SMS entry
728 ViewEntry smsEntry = new ViewEntry();
729 smsEntry.label = buildActionString(
730 R.string.actionText, displayLabel, true);
731 smsEntry.data = data;
732 smsEntry.id = id;
733 smsEntry.uri = uri;
734 smsEntry.intent = entry.auxIntent;
Evan Millar45e0ed32009-06-01 16:44:38 -0700735 smsEntry.mimetype = FastTrackWindow.MIME_SMS_ADDRESS;
Evan Millar66388be2009-05-28 15:41:07 -0700736 smsEntry.actionIcon = R.drawable.sym_action_sms;
737 mSmsEntries.add(smsEntry);
738 }
739 // Build email entries
740 } else if (mimetype.equals(CommonDataKinds.Email.CONTENT_ITEM_TYPE)) {
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800741 entry.label = buildActionString(R.string.actionEmail,
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.intent = new Intent(Intent.ACTION_SENDTO,
745 Uri.fromParts("mailto", data, null));
746 entry.actionIcon = android.R.drawable.sym_action_email;
Evan Millar45e0ed32009-06-01 16:44:38 -0700747 if (isSuperPrimary) {
Evan Millar66388be2009-05-28 15:41:07 -0700748 entry.primaryIcon = R.drawable.ic_default_number;
749 }
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800750 mEmailEntries.add(entry);
Evan Millar66388be2009-05-28 15:41:07 -0700751 // Build postal entries
752 } else if (mimetype.equals(CommonDataKinds.Postal.CONTENT_ITEM_TYPE)) {
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800753 entry.label = buildActionString(R.string.actionMap,
Evan Millar66388be2009-05-28 15:41:07 -0700754 ContactsUtils.getDisplayLabel(this, mimetype, type, label), true);
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800755 entry.data = data;
756 entry.maxLines = 4;
757 entry.intent = new Intent(Intent.ACTION_VIEW, uri);
758 entry.actionIcon = R.drawable.sym_action_map;
759 mPostalEntries.add(entry);
Evan Millar66388be2009-05-28 15:41:07 -0700760 // Build im entries
761 } else if (mimetype.equals(CommonDataKinds.Im.CONTENT_ITEM_TYPE)) {
762 String[] protocolStrings = getResources().getStringArray(
763 android.R.array.imProtocols);
764 Object protocolObj = ContactsUtils.decodeImProtocol(
765 aggCursor.getString(DATA_5_COLUMN));
Alex Kennberg87fc3172009-03-28 06:43:06 -0700766 String host = null;
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800767 if (protocolObj instanceof Number) {
768 int protocol = ((Number) protocolObj).intValue();
769 entry.label = buildActionString(R.string.actionChat,
770 protocolStrings[protocol], false);
Evan Millar66388be2009-05-28 15:41:07 -0700771 host = ContactsUtils.lookupProviderNameFromId(
772 protocol).toLowerCase();
773 if (protocol == CommonDataKinds.Im.PROTOCOL_GOOGLE_TALK
774 || protocol == CommonDataKinds.Im.PROTOCOL_MSN) {
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800775 entry.maxLabelLines = 2;
776 }
Alex Kennberg87fc3172009-03-28 06:43:06 -0700777 } else if (protocolObj != null) {
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800778 String providerName = (String) protocolObj;
779 entry.label = buildActionString(R.string.actionChat,
780 providerName, false);
781 host = providerName.toLowerCase();
782 }
783
784 // Only add the intent if there is a valid host
785 if (!TextUtils.isEmpty(host)) {
786 entry.intent = new Intent(Intent.ACTION_SENDTO,
787 constructImToUrl(host, data));
788 }
789 entry.data = data;
Evan Millar66388be2009-05-28 15:41:07 -0700790 //TODO(emillar) Do we want presence info?
791 /*if (!aggCursor.isNull(METHODS_STATUS_COLUMN)) {
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800792 entry.presenceIcon = Presence.getPresenceIconResourceId(
Evan Millar66388be2009-05-28 15:41:07 -0700793 aggCursor.getInt(METHODS_STATUS_COLUMN));
794 }*/
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800795 entry.actionIcon = android.R.drawable.sym_action_chat;
796 mImEntries.add(entry);
Evan Millar5c22c3b2009-05-29 11:37:54 -0700797 }
Evan Millar7e4accf2009-06-08 10:43:26 -0700798 // Set the name
799 } else if (mimetype.equals(CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE)) {
800 String name = mCursor.getString(DATA_9_COLUMN);
801 if (TextUtils.isEmpty(name)) {
802 mNameView.setText(getText(android.R.string.unknownName));
803 } else {
804 mNameView.setText(name);
805 }
806 /*
807 if (mPhoneticNameView != null) {
808 String phoneticName = mCursor.getString(CONTACT_PHONETIC_NAME_COLUMN);
809 mPhoneticNameView.setText(phoneticName);
810 }
811 */
Evan Millar66388be2009-05-28 15:41:07 -0700812 // Build organization entries
813 } else if (mimetype.equals(CommonDataKinds.Organization.CONTENT_ITEM_TYPE)) {
Evan Millar66388be2009-05-28 15:41:07 -0700814 final String company = aggCursor.getString(DATA_3_COLUMN);
815 final String title = aggCursor.getString(DATA_4_COLUMN);
816 final boolean isPrimary = "1".equals(aggCursor.getString(DATA_5_COLUMN));
817
818 if (isPrimary) {
819 entry.primaryIcon = R.drawable.ic_default_number;
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800820 }
Evan Millar5c22c3b2009-05-29 11:37:54 -0700821
Evan Millar66388be2009-05-28 15:41:07 -0700822 // Don't crash if the data is bogus
823 if (TextUtils.isEmpty(company) && TextUtils.isEmpty(title)) {
824 Log.w(TAG, "empty data for contact method " + id);
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800825 continue;
826 }
Evan Millar5c22c3b2009-05-29 11:37:54 -0700827
Evan Millar66388be2009-05-28 15:41:07 -0700828 entry.data = title;
829 entry.actionIcon = R.drawable.sym_action_organization;
830 entry.label = company;
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800831
Evan Millar66388be2009-05-28 15:41:07 -0700832 mOrganizationEntries.add(entry);
833 // Build note entries
834 } else if (mimetype.equals(CommonDataKinds.Note.CONTENT_ITEM_TYPE)) {
835 entry.label = getString(R.string.label_notes);
836 entry.data = aggCursor.getString(DATA_1_COLUMN);
837 entry.id = 0;
838 entry.uri = null;
839 entry.intent = null;
840 entry.maxLines = 10;
841 entry.actionIcon = R.drawable.sym_note;
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800842
Evan Millar66388be2009-05-28 15:41:07 -0700843 if (TextUtils.isEmpty(entry.data)) {
844 Log.w(TAG, "empty data for contact method " + id);
Alex Kennberg87fc3172009-03-28 06:43:06 -0700845 continue;
846 }
Evan Millar5c22c3b2009-05-29 11:37:54 -0700847
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800848 mOtherEntries.add(entry);
Evan Millar45e0ed32009-06-01 16:44:38 -0700849 // Load the photo
850 } else if (mimetype.equals(CommonDataKinds.Photo.CONTENT_ITEM_TYPE)) {
851 photoBitmap = ContactsUtils.loadContactPhoto(aggCursor, DATA_1_COLUMN, null);
Evan Millar66388be2009-05-28 15:41:07 -0700852 }
Evan Millar5c22c3b2009-05-29 11:37:54 -0700853
Evan Millar45e0ed32009-06-01 16:44:38 -0700854
Evan Millar66388be2009-05-28 15:41:07 -0700855 // TODO(emillar) Add group entries
856// // Build the group entries
857// final Uri groupsUri = Uri.withAppendedPath(mUri, GroupMembership.CONTENT_DIRECTORY);
858// Cursor groupCursor = mResolver.query(groupsUri, ContactsListActivity.GROUPS_PROJECTION,
859// null, null, Groups.DEFAULT_SORT_ORDER);
860// if (groupCursor != null) {
861// try {
862// StringBuilder sb = new StringBuilder();
863//
864// while (groupCursor.moveToNext()) {
865// String systemId = groupCursor.getString(
866// ContactsListActivity.GROUPS_COLUMN_INDEX_SYSTEM_ID);
867//
868// if (systemId != null || Groups.GROUP_MY_CONTACTS.equals(systemId)) {
869// continue;
870// }
871//
872// String name = groupCursor.getString(ContactsListActivity.GROUPS_COLUMN_INDEX_NAME);
873// if (!TextUtils.isEmpty(name)) {
874// if (sb.length() == 0) {
875// sb.append(name);
876// } else {
877// sb.append(getString(R.string.group_list, name));
878// }
879// }
880// }
881//
882// if (sb.length() > 0) {
883// ViewEntry entry = new ViewEntry();
884// entry.kind = ContactEntryAdapter.Entry.KIND_GROUP;
885// entry.label = getString(R.string.label_groups);
886// entry.data = sb.toString();
887// entry.intent = new Intent(Intent.ACTION_EDIT, mUri);
888//
889// // TODO: Add an icon for the groups item.
890//
891// mGroupEntries.add(entry);
892// }
893// } finally {
894// groupCursor.close();
895// }
896// }
Evan Millar5c22c3b2009-05-29 11:37:54 -0700897
Evan Millar66388be2009-05-28 15:41:07 -0700898 }
Evan Millar45e0ed32009-06-01 16:44:38 -0700899
900 if (photoBitmap == null) {
901 photoBitmap = ContactsUtils.loadPlaceholderPhoto(mNoPhotoResource, this, null);
902 }
903 mPhotoView.setImageBitmap(photoBitmap);
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800904 }
905 }
906
Evan Millar66388be2009-05-28 15:41:07 -0700907
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800908 String buildActionString(int actionResId, CharSequence type, boolean lowerCase) {
Jeff Hamilton8350e5b2009-03-24 21:01:34 -0700909 // If there is no type just display an empty string
910 if (type == null) {
911 type = "";
912 }
913
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800914 if (lowerCase) {
915 return getString(actionResId, type.toString().toLowerCase());
916 } else {
917 return getString(actionResId, type.toString());
918 }
919 }
Evan Millar5c22c3b2009-05-29 11:37:54 -0700920
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800921 /**
922 * A basic structure with the data for a contact entry in the list.
923 */
924 final static class ViewEntry extends ContactEntryAdapter.Entry {
925 public int primaryIcon = -1;
926 public Intent intent;
927 public Intent auxIntent = null;
928 public int presenceIcon = -1;
929 public int actionIcon = -1;
930 public int maxLabelLines = 1;
931 }
932
933 private static final class ViewAdapter extends ContactEntryAdapter<ViewEntry> {
934 /** Cache of the children views of a row */
935 static class ViewCache {
936 public TextView label;
937 public TextView data;
938 public ImageView actionIcon;
939 public ImageView presenceIcon;
Evan Millar5c22c3b2009-05-29 11:37:54 -0700940
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800941 // Need to keep track of this too
942 ViewEntry entry;
943 }
Evan Millar5c22c3b2009-05-29 11:37:54 -0700944
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800945 ViewAdapter(Context context, ArrayList<ArrayList<ViewEntry>> sections) {
946 super(context, sections, SHOW_SEPARATORS);
947 }
948
949 @Override
950 public View getView(int position, View convertView, ViewGroup parent) {
Evan Millar5c22c3b2009-05-29 11:37:54 -0700951 ViewEntry entry = getEntry(mSections, position, false);
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800952 View v;
953
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800954 ViewCache views;
955
956 // Check to see if we can reuse convertView
957 if (convertView != null) {
958 v = convertView;
959 views = (ViewCache) v.getTag();
960 } else {
961 // Create a new view if needed
962 v = mInflater.inflate(R.layout.list_item_text_icons, parent, false);
963
964 // Cache the children
965 views = new ViewCache();
966 views.label = (TextView) v.findViewById(android.R.id.text1);
967 views.data = (TextView) v.findViewById(android.R.id.text2);
968 views.actionIcon = (ImageView) v.findViewById(R.id.icon1);
969 views.presenceIcon = (ImageView) v.findViewById(R.id.icon2);
970 v.setTag(views);
971 }
972
973 // Update the entry in the view cache
974 views.entry = entry;
975
976 // Bind the data to the view
977 bindView(v, entry);
978 return v;
979 }
980
981 @Override
982 protected View newView(int position, ViewGroup parent) {
983 // getView() handles this
984 throw new UnsupportedOperationException();
985 }
986
987 @Override
988 protected void bindView(View view, ViewEntry entry) {
989 final Resources resources = mContext.getResources();
990 ViewCache views = (ViewCache) view.getTag();
991
992 // Set the label
993 TextView label = views.label;
994 setMaxLines(label, entry.maxLabelLines);
995 label.setText(entry.label);
996
997 // Set the data
998 TextView data = views.data;
999 if (data != null) {
1000 data.setText(entry.data);
1001 setMaxLines(data, entry.maxLines);
1002 }
1003
1004 // Set the action icon
1005 ImageView action = views.actionIcon;
1006 if (entry.actionIcon != -1) {
1007 action.setImageDrawable(resources.getDrawable(entry.actionIcon));
1008 action.setVisibility(View.VISIBLE);
1009 } else {
1010 // Things should still line up as if there was an icon, so make it invisible
1011 action.setVisibility(View.INVISIBLE);
1012 }
1013
1014 // Set the presence icon
1015 Drawable presenceIcon = null;
1016 if (entry.primaryIcon != -1) {
1017 presenceIcon = resources.getDrawable(entry.primaryIcon);
1018 } else if (entry.presenceIcon != -1) {
1019 presenceIcon = resources.getDrawable(entry.presenceIcon);
1020 }
1021
1022 ImageView presence = views.presenceIcon;
1023 if (presenceIcon != null) {
1024 presence.setImageDrawable(presenceIcon);
1025 presence.setVisibility(View.VISIBLE);
1026 } else {
1027 presence.setVisibility(View.GONE);
1028 }
1029 }
1030
1031 private void setMaxLines(TextView textView, int maxLines) {
1032 if (maxLines == 1) {
1033 textView.setSingleLine(true);
1034 textView.setEllipsize(TextUtils.TruncateAt.END);
1035 } else {
1036 textView.setSingleLine(false);
1037 textView.setMaxLines(maxLines);
1038 textView.setEllipsize(null);
1039 }
1040 }
1041 }
1042}