• 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                     StickyTabs.saveTab(this, getIntent());
130                     return true;
131                 }
132             }
133         }
134 
135         return super.onKeyDown(keyCode, event);
136     }
137 
138     /**
139      * Update user interface with details of given call.
140      *
141      * @param callUri Uri into {@link CallLog.Calls}
142      */
updateData(Uri callUri)143     private void updateData(Uri callUri) {
144         ContentResolver resolver = getContentResolver();
145         Cursor callCursor = resolver.query(callUri, CALL_LOG_PROJECTION, null, null, null);
146         try {
147             if (callCursor != null && callCursor.moveToFirst()) {
148                 // Read call log specifics
149                 mNumber = callCursor.getString(NUMBER_COLUMN_INDEX);
150                 long date = callCursor.getLong(DATE_COLUMN_INDEX);
151                 long duration = callCursor.getLong(DURATION_COLUMN_INDEX);
152                 int callType = callCursor.getInt(CALL_TYPE_COLUMN_INDEX);
153 
154                 // Pull out string in format [relative], [date]
155                 CharSequence dateClause = DateUtils.formatDateRange(this, date, date,
156                         DateUtils.FORMAT_SHOW_TIME | DateUtils.FORMAT_SHOW_DATE |
157                         DateUtils.FORMAT_SHOW_WEEKDAY | DateUtils.FORMAT_SHOW_YEAR);
158                 mCallTime.setText(dateClause);
159 
160                 // Set the duration
161                 if (callType == Calls.MISSED_TYPE) {
162                     mCallDuration.setVisibility(View.GONE);
163                 } else {
164                     mCallDuration.setVisibility(View.VISIBLE);
165                     mCallDuration.setText(formatDuration(duration));
166                 }
167 
168                 // Set the call type icon and caption
169                 String callText = null;
170                 switch (callType) {
171                     case Calls.INCOMING_TYPE:
172                         mCallTypeIcon.setImageResource(R.drawable.ic_call_log_header_incoming_call);
173                         mCallType.setText(R.string.type_incoming);
174                         callText = getString(R.string.callBack);
175                         break;
176 
177                     case Calls.OUTGOING_TYPE:
178                         mCallTypeIcon.setImageResource(R.drawable.ic_call_log_header_outgoing_call);
179                         mCallType.setText(R.string.type_outgoing);
180                         callText = getString(R.string.callAgain);
181                         break;
182 
183                     case Calls.MISSED_TYPE:
184                         mCallTypeIcon.setImageResource(R.drawable.ic_call_log_header_missed_call);
185                         mCallType.setText(R.string.type_missed);
186                         callText = getString(R.string.returnCall);
187                         break;
188                 }
189 
190                 if (mNumber.equals(CallerInfo.UNKNOWN_NUMBER) ||
191                         mNumber.equals(CallerInfo.PRIVATE_NUMBER)) {
192                     // List is empty, let the empty view show instead.
193                     TextView emptyText = (TextView) findViewById(R.id.emptyText);
194                     if (emptyText != null) {
195                         emptyText.setText(mNumber.equals(CallerInfo.PRIVATE_NUMBER)
196                                 ? R.string.private_num : R.string.unknown);
197                     }
198                 } else {
199                     // Perform a reverse-phonebook lookup to find the PERSON_ID
200                     String callLabel = null;
201                     Uri personUri = null;
202                     Uri phoneUri = Uri.withAppendedPath(PhoneLookup.CONTENT_FILTER_URI,
203                             Uri.encode(mNumber));
204                     Cursor phonesCursor = resolver.query(phoneUri, PHONES_PROJECTION, null, null, null);
205                     try {
206                         if (phonesCursor != null && phonesCursor.moveToFirst()) {
207                             long personId = phonesCursor.getLong(COLUMN_INDEX_ID);
208                             personUri = ContentUris.withAppendedId(
209                                     Contacts.CONTENT_URI, personId);
210                             callText = getString(R.string.recentCalls_callNumber,
211                                     phonesCursor.getString(COLUMN_INDEX_NAME));
212                             mNumber = PhoneNumberUtils.formatNumber(
213                                     phonesCursor.getString(COLUMN_INDEX_NUMBER));
214                             callLabel = Phone.getDisplayLabel(this,
215                                     phonesCursor.getInt(COLUMN_INDEX_TYPE),
216                                     phonesCursor.getString(COLUMN_INDEX_LABEL)).toString();
217                         } else {
218                             mNumber = PhoneNumberUtils.formatNumber(mNumber);
219                         }
220                     } finally {
221                         if (phonesCursor != null) phonesCursor.close();
222                     }
223 
224                     // Build list of various available actions
225                     List<ViewEntry> actions = new ArrayList<ViewEntry>();
226 
227                     Intent callIntent = new Intent(Intent.ACTION_CALL_PRIVILEGED,
228                             Uri.fromParts("tel", mNumber, null));
229                     ViewEntry entry = new ViewEntry(android.R.drawable.sym_action_call, callText,
230                             callIntent);
231                     entry.number = mNumber;
232                     entry.label = callLabel;
233                     actions.add(entry);
234 
235                     Intent smsIntent = new Intent(Intent.ACTION_SENDTO,
236                             Uri.fromParts("sms", mNumber, null));
237                     actions.add(new ViewEntry(R.drawable.sym_action_sms,
238                             getString(R.string.menu_sendTextMessage), smsIntent));
239 
240                     // Let user view contact details if they exist, otherwise add option
241                     // to create new contact from this number.
242                     if (personUri != null) {
243                         Intent viewIntent = new Intent(Intent.ACTION_VIEW, personUri);
244                         StickyTabs.setTab(viewIntent, getIntent());
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);
249                         createIntent.setType(Contacts.CONTENT_ITEM_TYPE);
250                         createIntent.putExtra(Insert.PHONE, mNumber);
251                         actions.add(new ViewEntry(R.drawable.sym_action_add,
252                                 getString(R.string.recentCalls_addToContact), createIntent));
253                     }
254 
255                     ViewAdapter adapter = new ViewAdapter(this, actions);
256                     setListAdapter(adapter);
257                 }
258             } else {
259                 // Something went wrong reading in our primary data, so we're going to
260                 // bail out and show error to users.
261                 Toast.makeText(this, R.string.toast_call_detail_error,
262                         Toast.LENGTH_SHORT).show();
263                 finish();
264             }
265         } finally {
266             if (callCursor != null) {
267                 callCursor.close();
268             }
269         }
270     }
271 
formatDuration(long elapsedSeconds)272     private String formatDuration(long elapsedSeconds) {
273         long minutes = 0;
274         long seconds = 0;
275 
276         if (elapsedSeconds >= 60) {
277             minutes = elapsedSeconds / 60;
278             elapsedSeconds -= minutes * 60;
279         }
280         seconds = elapsedSeconds;
281 
282         return getString(R.string.callDetailsDurationFormat, minutes, seconds);
283     }
284 
285     static final class ViewEntry {
286         public int icon = -1;
287         public String text = null;
288         public Intent intent = null;
289         public String label = null;
290         public String number = null;
291 
ViewEntry(int icon, String text, Intent intent)292         public ViewEntry(int icon, String text, Intent intent) {
293             this.icon = icon;
294             this.text = text;
295             this.intent = intent;
296         }
297     }
298 
299     static final class ViewAdapter extends BaseAdapter {
300 
301         private final List<ViewEntry> mActions;
302 
303         private final LayoutInflater mInflater;
304 
ViewAdapter(Context context, List<ViewEntry> actions)305         public ViewAdapter(Context context, List<ViewEntry> actions) {
306             mActions = actions;
307             mInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
308         }
309 
getCount()310         public int getCount() {
311             return mActions.size();
312         }
313 
getItem(int position)314         public Object getItem(int position) {
315             return mActions.get(position);
316         }
317 
getItemId(int position)318         public long getItemId(int position) {
319             return position;
320         }
321 
getView(int position, View convertView, ViewGroup parent)322         public View getView(int position, View convertView, ViewGroup parent) {
323             // Make sure we have a valid convertView to start with
324             if (convertView == null) {
325                 convertView = mInflater.inflate(R.layout.call_detail_list_item, parent, false);
326             }
327 
328             // Fill action with icon and text.
329             ViewEntry entry = mActions.get(position);
330             convertView.setTag(entry);
331 
332             ImageView icon = (ImageView) convertView.findViewById(R.id.icon);
333             TextView text = (TextView) convertView.findViewById(android.R.id.text1);
334 
335             icon.setImageResource(entry.icon);
336             text.setText(entry.text);
337 
338             View line2 = convertView.findViewById(R.id.line2);
339             boolean numberEmpty = TextUtils.isEmpty(entry.number);
340             boolean labelEmpty = TextUtils.isEmpty(entry.label) || numberEmpty;
341             if (labelEmpty && numberEmpty) {
342                 line2.setVisibility(View.GONE);
343             } else {
344                 line2.setVisibility(View.VISIBLE);
345 
346                 TextView label = (TextView) convertView.findViewById(R.id.label);
347                 if (labelEmpty) {
348                     label.setVisibility(View.GONE);
349                 } else {
350                     label.setText(entry.label);
351                     label.setVisibility(View.VISIBLE);
352                 }
353 
354                 TextView number = (TextView) convertView.findViewById(R.id.number);
355                 number.setText(entry.number);
356             }
357 
358             return convertView;
359         }
360     }
361 
onItemClick(AdapterView parent, View view, int position, long id)362     public void onItemClick(AdapterView parent, View view, int position, long id) {
363         // Handle passing action off to correct handler.
364         if (view.getTag() instanceof ViewEntry) {
365             ViewEntry entry = (ViewEntry) view.getTag();
366             if (entry.intent != null) {
367                 if (Intent.ACTION_CALL_PRIVILEGED.equals(entry.intent.getAction())) {
368                     StickyTabs.saveTab(this, getIntent());
369                 }
370                 startActivity(entry.intent);
371             }
372         }
373     }
374 
375     @Override
startSearch(String initialQuery, boolean selectInitialQuery, Bundle appSearchData, boolean globalSearch)376     public void startSearch(String initialQuery, boolean selectInitialQuery, Bundle appSearchData,
377             boolean globalSearch) {
378         if (globalSearch) {
379             super.startSearch(initialQuery, selectInitialQuery, appSearchData, globalSearch);
380         } else {
381             ContactsSearchManager.startSearch(this, initialQuery);
382         }
383     }
384 }
385