blob: 012a33ef6b9ef6627f95e43e4c30e77d94dd688a [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
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.Contacts;
30import android.provider.CallLog.Calls;
31import android.provider.Contacts.People;
32import android.provider.Contacts.Phones;
33import android.provider.Contacts.Intents.Insert;
34import android.telephony.PhoneNumberUtils;
35import android.telephony.TelephonyManager;
36import android.text.TextUtils;
37import android.text.format.DateUtils;
38import android.view.KeyEvent;
39import android.view.LayoutInflater;
40import android.view.View;
41import android.view.ViewGroup;
42import android.widget.AdapterView;
43import android.widget.BaseAdapter;
44import android.widget.ImageView;
45import android.widget.TextView;
46import android.widget.Toast;
47
48import java.util.ArrayList;
49import java.util.List;
50
51/**
52 * Displays the details of a specific call log entry.
53 */
54public class CallDetailActivity extends ListActivity implements
55 AdapterView.OnItemClickListener {
56 private static final String TAG = "CallDetail";
57
58 private TextView mCallType;
59 private ImageView mCallTypeIcon;
60 private TextView mCallTime;
61 private TextView mCallDuration;
62
63 private String mNumber = null;
64
65 /* package */ LayoutInflater mInflater;
66 /* package */ Resources mResources;
67
68 static final String[] CALL_LOG_PROJECTION = new String[] {
69 CallLog.Calls.DATE,
70 CallLog.Calls.DURATION,
71 CallLog.Calls.NUMBER,
72 CallLog.Calls.TYPE,
73 };
74
75 static final int DATE_COLUMN_INDEX = 0;
76 static final int DURATION_COLUMN_INDEX = 1;
77 static final int NUMBER_COLUMN_INDEX = 2;
78 static final int CALL_TYPE_COLUMN_INDEX = 3;
79
80 static final String[] PHONES_PROJECTION = new String[] {
81 Phones.PERSON_ID,
82 Phones.DISPLAY_NAME,
83 Phones.TYPE,
84 Phones.LABEL,
85 Phones.NUMBER,
86 };
87 static final int COLUMN_INDEX_ID = 0;
88 static final int COLUMN_INDEX_NAME = 1;
89 static final int COLUMN_INDEX_TYPE = 2;
90 static final int COLUMN_INDEX_LABEL = 3;
91 static final int COLUMN_INDEX_NUMBER = 4;
92
93 @Override
94 protected void onCreate(Bundle icicle) {
95 super.onCreate(icicle);
96
97 setContentView(R.layout.call_detail);
98
99 mInflater = (LayoutInflater) getSystemService(LAYOUT_INFLATER_SERVICE);
100 mResources = getResources();
101
102 mCallType = (TextView) findViewById(R.id.type);
103 mCallTypeIcon = (ImageView) findViewById(R.id.icon);
104 mCallTime = (TextView) findViewById(R.id.time);
105 mCallDuration = (TextView) findViewById(R.id.duration);
106
107 getListView().setOnItemClickListener(this);
108 }
109
110 @Override
111 public void onResume() {
112 super.onResume();
113 updateData(getIntent().getData());
114 }
115
116 @Override
117 public boolean onKeyDown(int keyCode, KeyEvent event) {
118 switch (keyCode) {
119 case KeyEvent.KEYCODE_CALL: {
120 // Make sure phone isn't already busy before starting direct call
121 TelephonyManager tm = (TelephonyManager)
122 getSystemService(Context.TELEPHONY_SERVICE);
123 if (tm.getCallState() == TelephonyManager.CALL_STATE_IDLE) {
124 Intent callIntent = new Intent(Intent.ACTION_CALL_PRIVILEGED,
125 Uri.fromParts("tel", mNumber, null));
126 startActivity(callIntent);
127 return true;
128 }
129 }
130 }
131
132 return super.onKeyDown(keyCode, event);
133 }
134
135 /**
136 * Update user interface with details of given call.
137 *
138 * @param callUri Uri into {@link CallLog.Calls}
139 */
140 private void updateData(Uri callUri) {
141 ContentResolver resolver = getContentResolver();
142 Cursor callCursor = resolver.query(callUri, CALL_LOG_PROJECTION, null, null, null);
143 try {
144 if (callCursor != null && callCursor.moveToFirst()) {
145 // Read call log specifics
146 mNumber = callCursor.getString(NUMBER_COLUMN_INDEX);
147 long date = callCursor.getLong(DATE_COLUMN_INDEX);
148 long duration = callCursor.getLong(DURATION_COLUMN_INDEX);
149 int callType = callCursor.getInt(CALL_TYPE_COLUMN_INDEX);
150
151 // Pull out string in format [relative], [date]
152 CharSequence dateClause = DateUtils.formatDateRange(this, date, date,
153 DateUtils.FORMAT_SHOW_TIME | DateUtils.FORMAT_SHOW_DATE |
154 DateUtils.FORMAT_SHOW_WEEKDAY | DateUtils.FORMAT_SHOW_YEAR);
155 mCallTime.setText(dateClause);
156
157 // Set the duration
158 if (callType == Calls.MISSED_TYPE) {
159 mCallDuration.setVisibility(View.GONE);
160 } else {
161 mCallDuration.setVisibility(View.VISIBLE);
162 mCallDuration.setText(formatDuration(duration));
163 }
164
165 // Set the call type icon and caption
166 String callText = null;
167 switch (callType) {
168 case Calls.INCOMING_TYPE:
169 mCallTypeIcon.setImageResource(android.R.drawable.sym_call_incoming);
170 mCallType.setText(R.string.type_incoming);
171 callText = getString(R.string.callBack);
172 break;
173
174 case Calls.OUTGOING_TYPE:
175 mCallTypeIcon.setImageResource(android.R.drawable.sym_call_outgoing);
176 mCallType.setText(R.string.type_outgoing);
177 callText = getString(R.string.callAgain);
178 break;
179
180 case Calls.MISSED_TYPE:
181 mCallTypeIcon.setImageResource(android.R.drawable.sym_call_missed);
182 mCallType.setText(R.string.type_missed);
183 callText = getString(R.string.returnCall);
184 break;
185 }
186
187 // Perform a reverse-phonebook lookup to find the PERSON_ID
188 String callLabel = null;
189 Uri personUri = null;
190 Uri phoneUri = Uri.withAppendedPath(Phones.CONTENT_FILTER_URL, Uri.encode(mNumber));
191 Cursor phonesCursor = resolver.query(phoneUri, PHONES_PROJECTION, null, null, null);
192 try {
193 if (phonesCursor != null && phonesCursor.moveToFirst()) {
194 long personId = phonesCursor.getLong(COLUMN_INDEX_ID);
195 personUri = ContentUris.withAppendedId(
196 Contacts.People.CONTENT_URI, personId);
197 callText = getString(R.string.recentCalls_callNumber,
198 phonesCursor.getString(COLUMN_INDEX_NAME));
199 mNumber = phonesCursor.getString(COLUMN_INDEX_NUMBER);
200 callLabel = Phones.getDisplayLabel(this,
201 phonesCursor.getInt(COLUMN_INDEX_TYPE),
202 phonesCursor.getString(COLUMN_INDEX_LABEL)).toString();
203 } else {
204 mNumber = PhoneNumberUtils.formatNumber(mNumber);
205 }
206 } finally {
207 if (phonesCursor != null) phonesCursor.close();
208 }
209
210 // Build list of various available actions
211 List<ViewEntry> actions = new ArrayList<ViewEntry>();
212
213 Intent callIntent = new Intent(Intent.ACTION_CALL_PRIVILEGED,
214 Uri.fromParts("tel", mNumber, null));
215 ViewEntry entry = new ViewEntry(android.R.drawable.sym_action_call, callText,
216 callIntent);
217 entry.number = mNumber;
218 entry.label = callLabel;
219 actions.add(entry);
220
221 Intent smsIntent = new Intent(Intent.ACTION_SENDTO,
222 Uri.fromParts("sms", mNumber, null));
223 actions.add(new ViewEntry(R.drawable.sym_action_sms,
224 getString(R.string.menu_sendTextMessage), smsIntent));
225
226 // Let user view contact details if they exist, otherwise add option
227 // to create new contact from this number.
228 if (personUri != null) {
229 Intent viewIntent = new Intent(Intent.ACTION_VIEW, personUri);
230 actions.add(new ViewEntry(R.drawable.sym_action_view_contact,
231 getString(R.string.menu_viewContact), viewIntent));
232 } else {
233 Intent createIntent = new Intent(Intent.ACTION_INSERT_OR_EDIT);
234 createIntent.setType(People.CONTENT_ITEM_TYPE);
235 createIntent.putExtra(Insert.PHONE, mNumber);
236 actions.add(new ViewEntry(R.drawable.sym_action_add,
237 getString(R.string.recentCalls_addToContact), createIntent));
238 }
239
240 ViewAdapter adapter = new ViewAdapter(this, actions);
241 setListAdapter(adapter);
242 } else {
243 // Something went wrong reading in our primary data, so we're going to
244 // bail out and show error to users.
245 Toast.makeText(this, R.string.toast_call_detail_error,
246 Toast.LENGTH_SHORT).show();
247 finish();
248 }
249 } finally {
250 if (callCursor != null) {
251 callCursor.close();
252 }
253 }
254 }
255
256 private String formatDuration(long elapsedSeconds) {
257 long minutes = 0;
258 long seconds = 0;
259
260 if (elapsedSeconds >= 60) {
261 minutes = elapsedSeconds / 60;
262 elapsedSeconds -= minutes * 60;
263 }
264 seconds = elapsedSeconds;
265
266 return getString(R.string.callDetailsDurationFormat, minutes, seconds);
267 }
268
269 static final class ViewEntry {
270 public int icon = -1;
271 public String text = null;
272 public Intent intent = null;
273 public String label = null;
274 public String number = null;
275
276 public ViewEntry(int icon, String text, Intent intent) {
277 this.icon = icon;
278 this.text = text;
279 this.intent = intent;
280 }
281 }
282
283 static final class ViewAdapter extends BaseAdapter {
284
285 private final List<ViewEntry> mActions;
286
287 private final LayoutInflater mInflater;
288
289 public ViewAdapter(Context context, List<ViewEntry> actions) {
290 mActions = actions;
291 mInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
292 }
293
294 public int getCount() {
295 return mActions.size();
296 }
297
298 public Object getItem(int position) {
299 return mActions.get(position);
300 }
301
302 public long getItemId(int position) {
303 return position;
304 }
305
306 public View getView(int position, View convertView, ViewGroup parent) {
307 // Make sure we have a valid convertView to start with
308 if (convertView == null) {
309 convertView = mInflater.inflate(R.layout.call_detail_list_item, parent, false);
310 }
311
312 // Fill action with icon and text.
313 ViewEntry entry = mActions.get(position);
314 convertView.setTag(entry);
315
316 ImageView icon = (ImageView) convertView.findViewById(R.id.icon);
317 TextView text = (TextView) convertView.findViewById(android.R.id.text1);
318
319 icon.setImageResource(entry.icon);
320 text.setText(entry.text);
321
322 View line2 = convertView.findViewById(R.id.line2);
323 boolean numberEmpty = TextUtils.isEmpty(entry.number);
324 boolean labelEmpty = TextUtils.isEmpty(entry.label) || numberEmpty;
325 if (labelEmpty && numberEmpty) {
326 line2.setVisibility(View.GONE);
327 } else {
328 line2.setVisibility(View.VISIBLE);
329
330 TextView label = (TextView) convertView.findViewById(R.id.label);
331 if (labelEmpty) {
332 label.setVisibility(View.GONE);
333 } else {
334 label.setText(entry.label);
335 label.setVisibility(View.VISIBLE);
336 }
337
338 TextView number = (TextView) convertView.findViewById(R.id.number);
339 number.setText(entry.number);
340 }
341
342 return convertView;
343 }
344 }
345
346 public void onItemClick(AdapterView parent, View view, int position, long id) {
347 // Handle passing action off to correct handler.
348 if (view.getTag() instanceof ViewEntry) {
349 ViewEntry entry = (ViewEntry) view.getTag();
350 if (entry.intent != null) {
351 startActivity(entry.intent);
352 }
353 }
354 }
355}