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