blob: f3f7073e9171614ec24d539b6204b4519cd5d15a [file] [log] [blame]
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08001/*
2 * Copyright (C) 2009 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
Amith Yamasani8bbe2f22009-03-24 21:24:12 -070019import com.android.internal.telephony.CallerInfo;
20
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -080021import android.app.ListActivity;
22import android.content.ContentResolver;
23import android.content.ContentUris;
24import android.content.Context;
25import android.content.Intent;
26import android.content.res.Resources;
27import android.database.Cursor;
28import android.net.Uri;
29import android.os.Bundle;
30import android.provider.CallLog;
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -080031import android.provider.CallLog.Calls;
Flavio Lerda9cafe472011-06-08 14:06:13 +010032import android.provider.Contacts.Intents.Insert;
Dmitri Plotnikovd0d776d2009-08-19 17:38:04 -070033import android.provider.ContactsContract.Contacts;
34import android.provider.ContactsContract.PhoneLookup;
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -080035import android.telephony.PhoneNumberUtils;
36import android.telephony.TelephonyManager;
37import android.text.TextUtils;
38import android.text.format.DateUtils;
39import android.view.KeyEvent;
40import android.view.LayoutInflater;
41import android.view.View;
42import android.view.ViewGroup;
43import android.widget.AdapterView;
44import android.widget.BaseAdapter;
45import android.widget.ImageView;
46import android.widget.TextView;
47import android.widget.Toast;
48
49import java.util.ArrayList;
50import java.util.List;
51
52/**
53 * Displays the details of a specific call log entry.
54 */
55public class CallDetailActivity extends ListActivity implements
56 AdapterView.OnItemClickListener {
57 private static final String TAG = "CallDetail";
58
Flavio Lerdaa024c3f2011-06-10 10:47:07 +010059 /** The views representing the details of a phone call. */
60 PhoneCallDetailsViews mPhoneCallDetailsViews;
Flavio Lerda9cafe472011-06-08 14:06:13 +010061 private TextView mCallTimeView;
62 private TextView mCallDurationView;
63 private View mCallActionView;
64 private ImageView mContactPhotoView;
65 private ImageView mContactBackgroundView;
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -080066
67 private String mNumber = null;
Bai Tao09eb04f2010-09-01 15:34:16 +080068 private String mDefaultCountryIso;
Dmitri Plotnikovd0d776d2009-08-19 17:38:04 -070069
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -080070 /* package */ LayoutInflater mInflater;
71 /* package */ Resources mResources;
Flavio Lerda9cafe472011-06-08 14:06:13 +010072 /** Helper to load contact photos. */
73 private ContactPhotoManager mContactPhotoManager;
74 /** Attached to the call action button in the UI. */
Dmitri Plotnikovd0d776d2009-08-19 17:38:04 -070075
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -080076 static final String[] CALL_LOG_PROJECTION = new String[] {
77 CallLog.Calls.DATE,
78 CallLog.Calls.DURATION,
79 CallLog.Calls.NUMBER,
80 CallLog.Calls.TYPE,
Bai Tao09eb04f2010-09-01 15:34:16 +080081 CallLog.Calls.COUNTRY_ISO,
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -080082 };
Dmitri Plotnikovd0d776d2009-08-19 17:38:04 -070083
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -080084 static final int DATE_COLUMN_INDEX = 0;
85 static final int DURATION_COLUMN_INDEX = 1;
86 static final int NUMBER_COLUMN_INDEX = 2;
87 static final int CALL_TYPE_COLUMN_INDEX = 3;
Bai Tao09eb04f2010-09-01 15:34:16 +080088 static final int COUNTRY_ISO_COLUMN_INDEX = 4;
Dmitri Plotnikovd0d776d2009-08-19 17:38:04 -070089
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -080090 static final String[] PHONES_PROJECTION = new String[] {
Dmitri Plotnikovd0d776d2009-08-19 17:38:04 -070091 PhoneLookup._ID,
92 PhoneLookup.DISPLAY_NAME,
93 PhoneLookup.TYPE,
94 PhoneLookup.LABEL,
95 PhoneLookup.NUMBER,
Bai Tao09eb04f2010-09-01 15:34:16 +080096 PhoneLookup.NORMALIZED_NUMBER,
Flavio Lerda9cafe472011-06-08 14:06:13 +010097 PhoneLookup.PHOTO_ID,
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -080098 };
99 static final int COLUMN_INDEX_ID = 0;
100 static final int COLUMN_INDEX_NAME = 1;
101 static final int COLUMN_INDEX_TYPE = 2;
102 static final int COLUMN_INDEX_LABEL = 3;
103 static final int COLUMN_INDEX_NUMBER = 4;
Bai Tao09eb04f2010-09-01 15:34:16 +0800104 static final int COLUMN_INDEX_NORMALIZED_NUMBER = 5;
Flavio Lerda9cafe472011-06-08 14:06:13 +0100105 static final int COLUMN_INDEX_PHOTO_ID = 6;
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800106
107 @Override
108 protected void onCreate(Bundle icicle) {
109 super.onCreate(icicle);
110
111 setContentView(R.layout.call_detail);
112
113 mInflater = (LayoutInflater) getSystemService(LAYOUT_INFLATER_SERVICE);
114 mResources = getResources();
Dmitri Plotnikovd0d776d2009-08-19 17:38:04 -0700115
Flavio Lerdaa024c3f2011-06-10 10:47:07 +0100116 mPhoneCallDetailsViews = new PhoneCallDetailsViews(getWindow().getDecorView());
Flavio Lerda9cafe472011-06-08 14:06:13 +0100117 mCallActionView = findViewById(R.id.call);
118 mContactPhotoView = (ImageView) findViewById(R.id.contact_photo);
119 mContactBackgroundView = (ImageView) findViewById(R.id.contact_background);
120 mCallTimeView = (TextView) findViewById(R.id.time);
121 mCallDurationView = (TextView) findViewById(R.id.duration);
Bai Tao09eb04f2010-09-01 15:34:16 +0800122 mDefaultCountryIso = ContactsUtils.getCurrentCountryIso(this);
Flavio Lerda9cafe472011-06-08 14:06:13 +0100123 mContactPhotoManager = ContactPhotoManager.getInstance(this);
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800124 getListView().setOnItemClickListener(this);
125 }
Dmitri Plotnikovd0d776d2009-08-19 17:38:04 -0700126
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800127 @Override
128 public void onResume() {
129 super.onResume();
130 updateData(getIntent().getData());
131 }
132
133 @Override
134 public boolean onKeyDown(int keyCode, KeyEvent event) {
135 switch (keyCode) {
136 case KeyEvent.KEYCODE_CALL: {
137 // Make sure phone isn't already busy before starting direct call
138 TelephonyManager tm = (TelephonyManager)
139 getSystemService(Context.TELEPHONY_SERVICE);
140 if (tm.getCallState() == TelephonyManager.CALL_STATE_IDLE) {
141 Intent callIntent = new Intent(Intent.ACTION_CALL_PRIVILEGED,
142 Uri.fromParts("tel", mNumber, null));
143 startActivity(callIntent);
144 return true;
145 }
146 }
147 }
Dmitri Plotnikovd0d776d2009-08-19 17:38:04 -0700148
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800149 return super.onKeyDown(keyCode, event);
150 }
Dmitri Plotnikovd0d776d2009-08-19 17:38:04 -0700151
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800152 /**
153 * Update user interface with details of given call.
Dmitri Plotnikovd0d776d2009-08-19 17:38:04 -0700154 *
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800155 * @param callUri Uri into {@link CallLog.Calls}
156 */
157 private void updateData(Uri callUri) {
158 ContentResolver resolver = getContentResolver();
159 Cursor callCursor = resolver.query(callUri, CALL_LOG_PROJECTION, null, null, null);
160 try {
161 if (callCursor != null && callCursor.moveToFirst()) {
162 // Read call log specifics
163 mNumber = callCursor.getString(NUMBER_COLUMN_INDEX);
164 long date = callCursor.getLong(DATE_COLUMN_INDEX);
165 long duration = callCursor.getLong(DURATION_COLUMN_INDEX);
166 int callType = callCursor.getInt(CALL_TYPE_COLUMN_INDEX);
Bai Tao09eb04f2010-09-01 15:34:16 +0800167 String countryIso = callCursor.getString(COUNTRY_ISO_COLUMN_INDEX);
168 if (TextUtils.isEmpty(countryIso)) {
169 countryIso = mDefaultCountryIso;
170 }
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800171 // Pull out string in format [relative], [date]
172 CharSequence dateClause = DateUtils.formatDateRange(this, date, date,
173 DateUtils.FORMAT_SHOW_TIME | DateUtils.FORMAT_SHOW_DATE |
174 DateUtils.FORMAT_SHOW_WEEKDAY | DateUtils.FORMAT_SHOW_YEAR);
Flavio Lerda9cafe472011-06-08 14:06:13 +0100175 mCallTimeView.setText(dateClause);
Dmitri Plotnikovd0d776d2009-08-19 17:38:04 -0700176
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800177 // Set the duration
178 if (callType == Calls.MISSED_TYPE) {
Flavio Lerda9cafe472011-06-08 14:06:13 +0100179 mCallDurationView.setVisibility(View.GONE);
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800180 } else {
Flavio Lerda9cafe472011-06-08 14:06:13 +0100181 mCallDurationView.setVisibility(View.VISIBLE);
182 mCallDurationView.setText(formatDuration(duration));
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800183 }
Dmitri Plotnikovd0d776d2009-08-19 17:38:04 -0700184
Flavio Lerda9cafe472011-06-08 14:06:13 +0100185 long photoId = 0L;
186 CharSequence nameText = "";
Flavio Lerdaa024c3f2011-06-10 10:47:07 +0100187 final CharSequence numberText;
188 int numberType = 0;
189 CharSequence numberLabel = "";
Amith Yamasani8bbe2f22009-03-24 21:24:12 -0700190 if (mNumber.equals(CallerInfo.UNKNOWN_NUMBER) ||
191 mNumber.equals(CallerInfo.PRIVATE_NUMBER)) {
Flavio Lerdaa024c3f2011-06-10 10:47:07 +0100192 numberText = getString(mNumber.equals(CallerInfo.PRIVATE_NUMBER)
Flavio Lerda9cafe472011-06-08 14:06:13 +0100193 ? R.string.private_num : R.string.unknown);
Flavio Lerda9cafe472011-06-08 14:06:13 +0100194 mCallActionView.setVisibility(View.GONE);
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800195 } else {
Amith Yamasani8bbe2f22009-03-24 21:24:12 -0700196 // Perform a reverse-phonebook lookup to find the PERSON_ID
Amith Yamasani8bbe2f22009-03-24 21:24:12 -0700197 Uri personUri = null;
Dmitri Plotnikovd0d776d2009-08-19 17:38:04 -0700198 Uri phoneUri = Uri.withAppendedPath(PhoneLookup.CONTENT_FILTER_URI,
199 Uri.encode(mNumber));
Flavio Lerda9cafe472011-06-08 14:06:13 +0100200 Cursor phonesCursor = resolver.query(
201 phoneUri, PHONES_PROJECTION, null, null, null);
Amith Yamasani8bbe2f22009-03-24 21:24:12 -0700202 try {
203 if (phonesCursor != null && phonesCursor.moveToFirst()) {
204 long personId = phonesCursor.getLong(COLUMN_INDEX_ID);
205 personUri = ContentUris.withAppendedId(
Dmitri Plotnikovd0d776d2009-08-19 17:38:04 -0700206 Contacts.CONTENT_URI, personId);
Flavio Lerda9cafe472011-06-08 14:06:13 +0100207 nameText = phonesCursor.getString(COLUMN_INDEX_NAME);
208 photoId = phonesCursor.getLong(COLUMN_INDEX_PHOTO_ID);
Sang-il, Lee177c77f2010-03-09 20:37:19 +0900209 mNumber = PhoneNumberUtils.formatNumber(
Bai Tao09eb04f2010-09-01 15:34:16 +0800210 phonesCursor.getString(COLUMN_INDEX_NUMBER),
211 phonesCursor.getString(COLUMN_INDEX_NORMALIZED_NUMBER),
212 countryIso);
Flavio Lerdaa024c3f2011-06-10 10:47:07 +0100213 numberType = phonesCursor.getInt(COLUMN_INDEX_TYPE);
214 numberLabel = phonesCursor.getString(COLUMN_INDEX_LABEL);
Amith Yamasani8bbe2f22009-03-24 21:24:12 -0700215 } else {
Bai Tao09eb04f2010-09-01 15:34:16 +0800216 mNumber = PhoneNumberUtils.formatNumber(mNumber, countryIso);
Amith Yamasani8bbe2f22009-03-24 21:24:12 -0700217 }
218 } finally {
Flavio Lerda9cafe472011-06-08 14:06:13 +0100219 if (phonesCursor != null) phonesCursor.close();
220 }
Flavio Lerdaa024c3f2011-06-10 10:47:07 +0100221 numberText = mNumber;
Flavio Lerda9cafe472011-06-08 14:06:13 +0100222
223 mCallActionView.setVisibility(View.VISIBLE);
224 mCallActionView.setOnClickListener(new View.OnClickListener() {
225 @Override
226 public void onClick(View v) {
227 Intent callIntent = new Intent(Intent.ACTION_CALL_PRIVILEGED,
228 Uri.fromParts("tel", mNumber, null));
229 startActivity(callIntent);
230 }
231 });
232
Amith Yamasani8bbe2f22009-03-24 21:24:12 -0700233 // Build list of various available actions
234 List<ViewEntry> actions = new ArrayList<ViewEntry>();
Dmitri Plotnikovd0d776d2009-08-19 17:38:04 -0700235
Amith Yamasani8bbe2f22009-03-24 21:24:12 -0700236 Intent smsIntent = new Intent(Intent.ACTION_SENDTO,
237 Uri.fromParts("sms", mNumber, null));
238 actions.add(new ViewEntry(R.drawable.sym_action_sms,
239 getString(R.string.menu_sendTextMessage), smsIntent));
Dmitri Plotnikovd0d776d2009-08-19 17:38:04 -0700240
Amith Yamasani8bbe2f22009-03-24 21:24:12 -0700241 // Let user view contact details if they exist, otherwise add option
242 // to create new contact from this number.
243 if (personUri != null) {
244 Intent viewIntent = new Intent(Intent.ACTION_VIEW, personUri);
245 actions.add(new ViewEntry(R.drawable.sym_action_view_contact,
246 getString(R.string.menu_viewContact), viewIntent));
247 } else {
248 Intent createIntent = new Intent(Intent.ACTION_INSERT_OR_EDIT);
Dmitri Plotnikovd0d776d2009-08-19 17:38:04 -0700249 createIntent.setType(Contacts.CONTENT_ITEM_TYPE);
Amith Yamasani8bbe2f22009-03-24 21:24:12 -0700250 createIntent.putExtra(Insert.PHONE, mNumber);
251 actions.add(new ViewEntry(R.drawable.sym_action_add,
252 getString(R.string.recentCalls_addToContact), createIntent));
253 }
Dmitri Plotnikovd0d776d2009-08-19 17:38:04 -0700254
Amith Yamasani8bbe2f22009-03-24 21:24:12 -0700255 ViewAdapter adapter = new ViewAdapter(this, actions);
256 setListAdapter(adapter);
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800257 }
Flavio Lerdaa024c3f2011-06-10 10:47:07 +0100258 mPhoneCallDetailsViews.setPhoneCallDetails(getResources(), date, callType, nameText,
259 numberText, numberType, numberLabel);
Flavio Lerda9cafe472011-06-08 14:06:13 +0100260
261 loadContactPhotos(photoId);
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800262 } else {
263 // Something went wrong reading in our primary data, so we're going to
264 // bail out and show error to users.
265 Toast.makeText(this, R.string.toast_call_detail_error,
266 Toast.LENGTH_SHORT).show();
267 finish();
268 }
269 } finally {
270 if (callCursor != null) {
271 callCursor.close();
272 }
273 }
274 }
275
Flavio Lerda9cafe472011-06-08 14:06:13 +0100276 /** Load the contact photos and places them in the corresponding views. */
277 private void loadContactPhotos(final long photoId) {
278 // There seem to be a limitation in the ContactPhotoManager that does not allow requesting
279 // two photos at once.
280 // TODO: Figure out the problem with ContactPhotoManager and remove this nonsense.
281 mContactPhotoView.post(new Runnable() {
282 @Override
283 public void run() {
284 mContactPhotoManager.loadPhoto(mContactPhotoView, photoId);
285 mContactPhotoView.postDelayed(new Runnable() {
286 @Override
287 public void run() {
288 mContactPhotoManager.loadPhoto(mContactBackgroundView, photoId);
289 }
290 }, 100);
291 }
292 });
293 }
294
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800295 private String formatDuration(long elapsedSeconds) {
296 long minutes = 0;
297 long seconds = 0;
298
299 if (elapsedSeconds >= 60) {
300 minutes = elapsedSeconds / 60;
301 elapsedSeconds -= minutes * 60;
302 }
303 seconds = elapsedSeconds;
304
305 return getString(R.string.callDetailsDurationFormat, minutes, seconds);
306 }
307
308 static final class ViewEntry {
309 public int icon = -1;
310 public String text = null;
311 public Intent intent = null;
312 public String label = null;
313 public String number = null;
Dmitri Plotnikovd0d776d2009-08-19 17:38:04 -0700314
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800315 public ViewEntry(int icon, String text, Intent intent) {
316 this.icon = icon;
317 this.text = text;
318 this.intent = intent;
319 }
320 }
321
322 static final class ViewAdapter extends BaseAdapter {
Dmitri Plotnikovd0d776d2009-08-19 17:38:04 -0700323
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800324 private final List<ViewEntry> mActions;
Dmitri Plotnikovd0d776d2009-08-19 17:38:04 -0700325
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800326 private final LayoutInflater mInflater;
Dmitri Plotnikovd0d776d2009-08-19 17:38:04 -0700327
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800328 public ViewAdapter(Context context, List<ViewEntry> actions) {
329 mActions = actions;
330 mInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
331 }
Dmitri Plotnikovd0d776d2009-08-19 17:38:04 -0700332
Flavio Lerda9cafe472011-06-08 14:06:13 +0100333 @Override
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800334 public int getCount() {
335 return mActions.size();
336 }
337
Flavio Lerda9cafe472011-06-08 14:06:13 +0100338 @Override
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800339 public Object getItem(int position) {
340 return mActions.get(position);
341 }
342
Flavio Lerda9cafe472011-06-08 14:06:13 +0100343 @Override
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800344 public long getItemId(int position) {
345 return position;
346 }
Dmitri Plotnikovd0d776d2009-08-19 17:38:04 -0700347
Flavio Lerda9cafe472011-06-08 14:06:13 +0100348 @Override
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800349 public View getView(int position, View convertView, ViewGroup parent) {
350 // Make sure we have a valid convertView to start with
351 if (convertView == null) {
352 convertView = mInflater.inflate(R.layout.call_detail_list_item, parent, false);
353 }
354
355 // Fill action with icon and text.
356 ViewEntry entry = mActions.get(position);
357 convertView.setTag(entry);
Dmitri Plotnikovd0d776d2009-08-19 17:38:04 -0700358
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800359 ImageView icon = (ImageView) convertView.findViewById(R.id.icon);
360 TextView text = (TextView) convertView.findViewById(android.R.id.text1);
361
362 icon.setImageResource(entry.icon);
363 text.setText(entry.text);
364
365 View line2 = convertView.findViewById(R.id.line2);
366 boolean numberEmpty = TextUtils.isEmpty(entry.number);
367 boolean labelEmpty = TextUtils.isEmpty(entry.label) || numberEmpty;
368 if (labelEmpty && numberEmpty) {
369 line2.setVisibility(View.GONE);
370 } else {
371 line2.setVisibility(View.VISIBLE);
Dmitri Plotnikovd0d776d2009-08-19 17:38:04 -0700372
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800373 TextView label = (TextView) convertView.findViewById(R.id.label);
374 if (labelEmpty) {
375 label.setVisibility(View.GONE);
376 } else {
377 label.setText(entry.label);
378 label.setVisibility(View.VISIBLE);
379 }
380
381 TextView number = (TextView) convertView.findViewById(R.id.number);
382 number.setText(entry.number);
383 }
Dmitri Plotnikovd0d776d2009-08-19 17:38:04 -0700384
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800385 return convertView;
386 }
387 }
Dmitri Plotnikovd0d776d2009-08-19 17:38:04 -0700388
Flavio Lerda9cafe472011-06-08 14:06:13 +0100389 @Override
390 public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800391 // Handle passing action off to correct handler.
392 if (view.getTag() instanceof ViewEntry) {
393 ViewEntry entry = (ViewEntry) view.getTag();
394 if (entry.intent != null) {
395 startActivity(entry.intent);
396 }
397 }
Dmitri Plotnikovd0d776d2009-08-19 17:38:04 -0700398 }
Dmitri Plotnikov8e86b752010-02-22 17:47:57 -0800399
400 @Override
401 public void startSearch(String initialQuery, boolean selectInitialQuery, Bundle appSearchData,
402 boolean globalSearch) {
403 if (globalSearch) {
404 super.startSearch(initialQuery, selectInitialQuery, appSearchData, globalSearch);
405 } else {
406 ContactsSearchManager.startSearch(this, initialQuery);
407 }
408 }
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800409}