blob: 8b25c09c857d8d0ff90d9a4388d2315c9035edae [file] [log] [blame]
The Android Open Source Projectad5ad712009-01-15 16:12:13 -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
19import android.app.ListActivity;
20import android.content.ContentResolver;
21import android.content.ContentUris;
22import android.content.Context;
23import android.content.Intent;
24import android.content.res.Resources;
25import android.database.Cursor;
26import android.net.Uri;
27import android.os.Bundle;
28import android.provider.CallLog;
29import android.provider.CallLog.Calls;
30import android.provider.Contacts;
31import android.provider.Contacts.Intents.Insert;
32import android.provider.Contacts.People;
33import android.provider.Contacts.Phones;
34import android.telephony.TelephonyManager;
35import android.text.format.DateUtils;
36import android.util.Log;
37import android.view.KeyEvent;
38import android.view.LayoutInflater;
39import android.view.View;
40import android.view.ViewGroup;
41import android.widget.AdapterView;
42import android.widget.BaseAdapter;
43import android.widget.ImageView;
44import android.widget.TextView;
45import android.widget.Toast;
46
47import java.util.LinkedList;
48import java.util.List;
49
50/**
51 * Displays the details of a specific call log entry.
52 */
53public class CallDetailActivity extends ListActivity implements
54 AdapterView.OnItemClickListener {
55 private static final String TAG = "CallDetail";
56
57 private Uri mUri;
58
59 private View mCallDetailItem;
60
61 private TextView mCallType;
62 private ImageView mCallTypeIcon;
63 private TextView mCallTime;
64 private View mCallDurationRow;
65 private TextView mCallDuration;
66
67 private String mNumber = null;
68
69 /* package */ LayoutInflater mInflater;
70 /* package */ Resources mResources;
71
72 static final String[] CALL_LOG_PROJECTION = new String[] {
73 CallLog.Calls.DATE,
74 CallLog.Calls.DURATION,
75 CallLog.Calls.NUMBER,
76 CallLog.Calls.TYPE,
77 };
78
79 static final int DATE_COLUMN_INDEX = 0;
80 static final int DURATION_COLUMN_INDEX = 1;
81 static final int NUMBER_COLUMN_INDEX = 2;
82 static final int CALL_TYPE_COLUMN_INDEX = 3;
83
84 static final String[] PHONES_PROJECTION = new String[] {
85 Phones.PERSON_ID,
86 };
87
88 static final int PERSON_ID_COLUMN_INDEX = 0;
89
90 private static final int INVALID_TYPE = -1;
91
92 @Override
93 protected void onCreate(Bundle icicle) {
94 super.onCreate(icicle);
95
96 setContentView(R.layout.call_detail);
97
98 mInflater = (LayoutInflater) getSystemService(LAYOUT_INFLATER_SERVICE);
99 mResources = getResources();
100
101 mCallDetailItem = mInflater.inflate(R.layout.call_detail_item, getListView(), false);
102
103 mCallType = (TextView) mCallDetailItem.findViewById(R.id.call_type);
104 mCallTypeIcon = (ImageView) mCallDetailItem.findViewById(R.id.call_type_icon);
105 mCallTime = (TextView) mCallDetailItem.findViewById(R.id.call_time);
106 mCallDurationRow = mCallDetailItem.findViewById(R.id.call_duration_row);
107 mCallDuration = (TextView) mCallDetailItem.findViewById(R.id.call_duration);
108
109 getListView().setOnItemClickListener(this);
110 }
111
112 @Override
113 public void onResume() {
114 super.onResume();
115 updateData(getIntent().getData());
116 }
117
118 @Override
119 public boolean onKeyDown(int keyCode, KeyEvent event) {
120 switch (keyCode) {
121 case KeyEvent.KEYCODE_CALL: {
122 // Make sure phone isn't already busy before starting direct call
123 TelephonyManager tm = (TelephonyManager)
124 getSystemService(Context.TELEPHONY_SERVICE);
125 if (tm.getCallState() == TelephonyManager.CALL_STATE_IDLE) {
126 Intent callIntent = new Intent(Intent.ACTION_CALL_PRIVILEGED,
127 Uri.fromParts("tel", mNumber, null));
128 startActivity(callIntent);
129 return true;
130 }
131 }
132 }
133
134 return super.onKeyDown(keyCode, event);
135 }
136
137 /**
138 * Try a reverse-phonebook lookup to find the contact, if any, behind the given number.
139 *
140 * @param number Phone number to perform reverse-lookup against
141 * @return Uri into {@link Contacts.People} if found, otherwise null
142 */
143 private Uri getPersonUri(String number) {
144 Uri personUri = null;
145
146 // Perform a reverse-phonebook lookup to find the PERSON_ID
147 ContentResolver resolver = getContentResolver();
148 Uri phoneUri = Uri.withAppendedPath(Phones.CONTENT_FILTER_URL, Uri.encode(number));
149 Cursor phonesCursor = resolver.query(phoneUri, PHONES_PROJECTION, null, null, null);
150 try {
151 if (phonesCursor != null && phonesCursor.moveToFirst()) {
152 long personId = phonesCursor.getLong(PERSON_ID_COLUMN_INDEX);
153 personUri = ContentUris.withAppendedId(Contacts.People.CONTENT_URI, personId);
154 }
155 } finally {
156 if (phonesCursor != null) {
157 phonesCursor.close();
158 }
159 }
160
161 return personUri;
162 }
163
164 /**
165 * Update user interface with details of given call.
166 *
167 * @param callUri Uri into {@link CallLog.Calls}
168 */
169 private void updateData(Uri callUri) {
170 ContentResolver resolver = getContentResolver();
171 Cursor callCursor = resolver.query(callUri, CALL_LOG_PROJECTION, null, null, null);
172 try {
173 if (callCursor != null && callCursor.moveToFirst()) {
174 // Read call log specifics
175 mNumber = callCursor.getString(NUMBER_COLUMN_INDEX);
176 long date = callCursor.getLong(DATE_COLUMN_INDEX);
177 long duration = callCursor.getLong(DURATION_COLUMN_INDEX);
178 int callType = callCursor.getInt(CALL_TYPE_COLUMN_INDEX);
179
180 // Pull out string in format [relative], [date]
181 CharSequence dateClause = DateUtils.formatDateRange(this, date, date,
182 DateUtils.FORMAT_SHOW_TIME | DateUtils.FORMAT_SHOW_DATE |
183 DateUtils.FORMAT_SHOW_WEEKDAY | DateUtils.FORMAT_SHOW_YEAR |
184 DateUtils.FORMAT_ABBREV_ALL);
185 long now = System.currentTimeMillis();
186 CharSequence relativeClause = DateUtils.getRelativeTimeSpanString(date, now,
187 DateUtils.SECOND_IN_MILLIS, DateUtils.FORMAT_ABBREV_RELATIVE);
188 String dateString = getString(R.string.datetime_relative,
189 dateClause, relativeClause);
190 mCallTime.setText(dateString);
191
192 // Set the duration
193 if (callType == Calls.MISSED_TYPE) {
194 mCallDurationRow.setVisibility(View.GONE);
195 } else {
196 mCallDurationRow.setVisibility(View.VISIBLE);
197 mCallDuration.setText(DateUtils.formatElapsedTime(duration));
198 }
199
200 // Set the call type icon and caption
201 switch (callType) {
202 case Calls.INCOMING_TYPE:
203 mCallTypeIcon.setImageResource(android.R.drawable.sym_call_incoming);
204 mCallType.setText(R.string.type_incoming);
205 break;
206
207 case Calls.OUTGOING_TYPE:
208 mCallTypeIcon.setImageResource(android.R.drawable.sym_call_outgoing);
209 mCallType.setText(R.string.type_outgoing);
210 break;
211
212 case Calls.MISSED_TYPE:
213 mCallTypeIcon.setImageResource(android.R.drawable.sym_call_missed);
214 mCallType.setText(R.string.type_missed);
215 break;
216 }
217
218 // Build list of various available actions
219 List<ViewEntry> actions = new LinkedList<ViewEntry>();
220
221 Intent callIntent = new Intent(Intent.ACTION_CALL_PRIVILEGED,
222 Uri.fromParts("tel", mNumber, null));
223 actions.add(new ViewEntry(R.drawable.ic_dialer_fork_current_call,
224 getString(R.string.recentCalls_callNumber, mNumber), callIntent));
225
226 Intent smsIntent = new Intent(Intent.ACTION_SENDTO,
227 Uri.fromParts("sms", mNumber, null));
228 actions.add(new ViewEntry(R.drawable.sym_action_sms,
229 getString(R.string.menu_sendTextMessage), smsIntent));
230
231 // Let user view contact details if they exist, otherwise add option
232 // to create new contact from this number.
233 Uri personUri = getPersonUri(mNumber);
234
235 if (personUri != null) {
236 Intent viewIntent = new Intent(Intent.ACTION_VIEW, personUri);
237 actions.add(new ViewEntry(R.drawable.ic_tab_unselected_contacts,
238 getString(R.string.menu_viewContact), viewIntent));
239 } else {
240 Intent createIntent = new Intent(Intent.ACTION_INSERT_OR_EDIT);
241 createIntent.setType(People.CONTENT_ITEM_TYPE);
242 createIntent.putExtra(Insert.PHONE, mNumber);
243 actions.add(new ViewEntry(R.drawable.ic_dialer_fork_add_call,
244 getString(R.string.recentCalls_addToContact), createIntent));
245 }
246
247 ViewAdapter adapter = new ViewAdapter(this, mCallDetailItem, actions);
248 setListAdapter(adapter);
249 } else {
250 // Something went wrong reading in our primary data, so we're going to
251 // bail out and show error to users.
252 Toast.makeText(this, R.string.toast_call_detail_error,
253 Toast.LENGTH_SHORT).show();
254 finish();
255 }
256 } finally {
257 if (callCursor != null) {
258 callCursor.close();
259 }
260 }
261 }
262
263 static final class ViewEntry {
264 public int icon = -1;
265 public String text = null;
266 public Intent intent = null;
267
268 public ViewEntry(int icon, String text, Intent intent) {
269 this.icon = icon;
270 this.text = text;
271 this.intent = intent;
272 }
273 }
274
275 static final class ViewAdapter extends BaseAdapter {
276
277 private final View mCallDetailItem;
278 private final List<ViewEntry> mActions;
279
280 private final Context mContext;
281 private final LayoutInflater mInflater;
282
283 public ViewAdapter(Context context, View callDetailItem, List<ViewEntry> actions) {
284 mCallDetailItem = callDetailItem;
285 mActions = actions;
286
287 mContext = context;
288 mInflater = (LayoutInflater) context
289 .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
290
291 }
292
293 public int getCount() {
294 // Count is actions plus two headers and call details panel.
295 return mActions.size() + 2;
296 }
297
298 public Object getItem(int position) {
299 if (position >= POS_FIRST_ITEM) {
300 return mActions.get(position - POS_FIRST_ITEM);
301 }
302 return null;
303 }
304
305 public long getItemId(int position) {
306 return position;
307 }
308
309 private static final int TYPE_HEADER = 0;
310 private static final int TYPE_CALL_DETAILS = 1;
311 private static final int TYPE_ACTION = 2;
312
313 private static final int POS_CALL_DETAILS = 0;
314 private static final int POS_ACTIONS_HEADER = 1;
315 private static final int POS_FIRST_ITEM = 2;
316
317 public int getViewTypeCount() {
318 // Types are headers, call details panel, and actions.
319 return 3;
320 }
321
322 public int getItemViewType(int position) {
323 switch(position) {
324 case POS_CALL_DETAILS:
325 return TYPE_CALL_DETAILS;
326 case POS_ACTIONS_HEADER:
327 return TYPE_HEADER;
328 default:
329 return TYPE_ACTION;
330 }
331 }
332
333 public boolean areAllItemsEnabled() {
334 return false;
335 }
336
337 public boolean isEnabled(int position) {
338 return (position > POS_ACTIONS_HEADER);
339 }
340
341 public View getView(int position, View convertView, ViewGroup parent) {
342 // Make sure we have a valid convertView to start with
343 if (convertView == null) {
344 switch(getItemViewType(position)) {
345 case TYPE_HEADER: {
346 convertView = mInflater.inflate(R.layout.list_separator, parent, false);
347 break;
348 }
349 case TYPE_CALL_DETAILS: {
350 convertView = mCallDetailItem;
351 break;
352 }
353 case TYPE_ACTION: {
354 convertView = mInflater.inflate(R.layout.dialpad_chooser_list_item,
355 parent, false);
356 break;
357 }
358 }
359 }
360
361 // Now fill our known-good convertView with data
362 switch(position) {
363 case POS_CALL_DETAILS: {
364 // Assume mCallDetailItem is already filled with correct data.
365 break;
366 }
367 case POS_ACTIONS_HEADER: {
368 TextView textView = (TextView) convertView;
369 textView.setText(mContext.getResources().getString(
370 R.string.header_actions));
371 break;
372 }
373 default: {
374 // Fill action with icon and text.
375 ViewEntry entry = (ViewEntry) getItem(position);
376 convertView.setTag(entry);
377
378 ImageView icon = (ImageView) convertView.findViewById(R.id.icon);
379 TextView text = (TextView) convertView.findViewById(R.id.text);
380
381 icon.setImageResource(entry.icon);
382 text.setText(entry.text);
383
384 break;
385 }
386 }
387
388 return convertView;
389 }
390
391 }
392
393 public void onItemClick(AdapterView parent, View view, int position, long id) {
394 // Handle passing action off to correct handler.
395 if (view.getTag() instanceof ViewEntry) {
396 ViewEntry entry = (ViewEntry) view.getTag();
397 if (entry.intent != null) {
398 startActivity(entry.intent);
399 }
400 }
401 }
402}