blob: 0931e3d760312e869c009b068c76d00a80b8391f [file] [log] [blame]
/*
* Copyright (C) 2009 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.contacts;
import com.android.contacts.format.FormatUtils;
import com.android.internal.telephony.CallerInfo;
import android.app.ListActivity;
import android.content.ContentResolver;
import android.content.ContentUris;
import android.content.Context;
import android.content.Intent;
import android.content.res.Resources;
import android.database.Cursor;
import android.graphics.Typeface;
import android.net.Uri;
import android.os.Bundle;
import android.provider.CallLog;
import android.provider.CallLog.Calls;
import android.provider.Contacts.Intents.Insert;
import android.provider.ContactsContract.CommonDataKinds.Phone;
import android.provider.ContactsContract.Contacts;
import android.provider.ContactsContract.PhoneLookup;
import android.telephony.PhoneNumberUtils;
import android.telephony.TelephonyManager;
import android.text.Spanned;
import android.text.TextUtils;
import android.text.format.DateUtils;
import android.view.KeyEvent;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.BaseAdapter;
import android.widget.ImageView;
import android.widget.TextView;
import android.widget.Toast;
import java.util.ArrayList;
import java.util.List;
/**
* Displays the details of a specific call log entry.
*/
public class CallDetailActivity extends ListActivity implements
AdapterView.OnItemClickListener {
private static final String TAG = "CallDetail";
private TextView mNameView;
private TextView mCallTypeView;
private TextView mNumberView;
private TextView mCallTimeView;
private TextView mCallDurationView;
private View mCallActionView;
private ImageView mContactPhotoView;
private ImageView mContactBackgroundView;
private String mNumber = null;
private String mDefaultCountryIso;
/* package */ LayoutInflater mInflater;
/* package */ Resources mResources;
/** Helper to load contact photos. */
private ContactPhotoManager mContactPhotoManager;
/** Attached to the call action button in the UI. */
static final String[] CALL_LOG_PROJECTION = new String[] {
CallLog.Calls.DATE,
CallLog.Calls.DURATION,
CallLog.Calls.NUMBER,
CallLog.Calls.TYPE,
CallLog.Calls.COUNTRY_ISO,
};
static final int DATE_COLUMN_INDEX = 0;
static final int DURATION_COLUMN_INDEX = 1;
static final int NUMBER_COLUMN_INDEX = 2;
static final int CALL_TYPE_COLUMN_INDEX = 3;
static final int COUNTRY_ISO_COLUMN_INDEX = 4;
static final String[] PHONES_PROJECTION = new String[] {
PhoneLookup._ID,
PhoneLookup.DISPLAY_NAME,
PhoneLookup.TYPE,
PhoneLookup.LABEL,
PhoneLookup.NUMBER,
PhoneLookup.NORMALIZED_NUMBER,
PhoneLookup.PHOTO_ID,
};
static final int COLUMN_INDEX_ID = 0;
static final int COLUMN_INDEX_NAME = 1;
static final int COLUMN_INDEX_TYPE = 2;
static final int COLUMN_INDEX_LABEL = 3;
static final int COLUMN_INDEX_NUMBER = 4;
static final int COLUMN_INDEX_NORMALIZED_NUMBER = 5;
static final int COLUMN_INDEX_PHOTO_ID = 6;
@Override
protected void onCreate(Bundle icicle) {
super.onCreate(icicle);
setContentView(R.layout.call_detail);
mInflater = (LayoutInflater) getSystemService(LAYOUT_INFLATER_SERVICE);
mResources = getResources();
mNameView = (TextView) findViewById(R.id.name);
mCallTypeView = (TextView) findViewById(R.id.call_type);
mNumberView = (TextView) findViewById(R.id.number);
mCallActionView = findViewById(R.id.call);
mContactPhotoView = (ImageView) findViewById(R.id.contact_photo);
mContactBackgroundView = (ImageView) findViewById(R.id.contact_background);
mCallTimeView = (TextView) findViewById(R.id.time);
mCallDurationView = (TextView) findViewById(R.id.duration);
mDefaultCountryIso = ContactsUtils.getCurrentCountryIso(this);
mContactPhotoManager = ContactPhotoManager.getInstance(this);
getListView().setOnItemClickListener(this);
}
@Override
public void onResume() {
super.onResume();
updateData(getIntent().getData());
}
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
switch (keyCode) {
case KeyEvent.KEYCODE_CALL: {
// Make sure phone isn't already busy before starting direct call
TelephonyManager tm = (TelephonyManager)
getSystemService(Context.TELEPHONY_SERVICE);
if (tm.getCallState() == TelephonyManager.CALL_STATE_IDLE) {
Intent callIntent = new Intent(Intent.ACTION_CALL_PRIVILEGED,
Uri.fromParts("tel", mNumber, null));
startActivity(callIntent);
return true;
}
}
}
return super.onKeyDown(keyCode, event);
}
/**
* Update user interface with details of given call.
*
* @param callUri Uri into {@link CallLog.Calls}
*/
private void updateData(Uri callUri) {
ContentResolver resolver = getContentResolver();
Cursor callCursor = resolver.query(callUri, CALL_LOG_PROJECTION, null, null, null);
try {
if (callCursor != null && callCursor.moveToFirst()) {
// Read call log specifics
mNumber = callCursor.getString(NUMBER_COLUMN_INDEX);
long date = callCursor.getLong(DATE_COLUMN_INDEX);
long duration = callCursor.getLong(DURATION_COLUMN_INDEX);
int callType = callCursor.getInt(CALL_TYPE_COLUMN_INDEX);
String countryIso = callCursor.getString(COUNTRY_ISO_COLUMN_INDEX);
if (TextUtils.isEmpty(countryIso)) {
countryIso = mDefaultCountryIso;
}
// Pull out string in format [relative], [date]
CharSequence dateClause = DateUtils.formatDateRange(this, date, date,
DateUtils.FORMAT_SHOW_TIME | DateUtils.FORMAT_SHOW_DATE |
DateUtils.FORMAT_SHOW_WEEKDAY | DateUtils.FORMAT_SHOW_YEAR);
mCallTimeView.setText(dateClause);
// Set the duration
if (callType == Calls.MISSED_TYPE) {
mCallDurationView.setVisibility(View.GONE);
} else {
mCallDurationView.setVisibility(View.VISIBLE);
mCallDurationView.setText(formatDuration(duration));
}
CharSequence shortDateText =
DateUtils.getRelativeTimeSpanString(date,
System.currentTimeMillis(),
DateUtils.MINUTE_IN_MILLIS,
DateUtils.FORMAT_ABBREV_RELATIVE);
CharSequence callTypeText = "";
switch (callType) {
case Calls.INCOMING_TYPE:
callTypeText = getString(R.string.type_incoming);
break;
case Calls.OUTGOING_TYPE:
callTypeText = getString(R.string.type_outgoing);
break;
case Calls.MISSED_TYPE:
callTypeText = getString(R.string.type_missed);
break;
}
mCallTypeView.setText(
getString(R.string.call_type_and_date,
FormatUtils.applyStyleToSpan(Typeface.BOLD,
callTypeText, 0, callTypeText.length(),
Spanned.SPAN_EXCLUSIVE_EXCLUSIVE),
shortDateText));
long photoId = 0L;
CharSequence nameText = "";
CharSequence numberText = "";
if (mNumber.equals(CallerInfo.UNKNOWN_NUMBER) ||
mNumber.equals(CallerInfo.PRIVATE_NUMBER)) {
nameText = getString(mNumber.equals(CallerInfo.PRIVATE_NUMBER)
? R.string.private_num : R.string.unknown);
numberText = "";
mCallActionView.setVisibility(View.GONE);
} else {
// Perform a reverse-phonebook lookup to find the PERSON_ID
CharSequence callLabel = null;
Uri personUri = null;
Uri phoneUri = Uri.withAppendedPath(PhoneLookup.CONTENT_FILTER_URI,
Uri.encode(mNumber));
Cursor phonesCursor = resolver.query(
phoneUri, PHONES_PROJECTION, null, null, null);
try {
if (phonesCursor != null && phonesCursor.moveToFirst()) {
long personId = phonesCursor.getLong(COLUMN_INDEX_ID);
personUri = ContentUris.withAppendedId(
Contacts.CONTENT_URI, personId);
nameText = phonesCursor.getString(COLUMN_INDEX_NAME);
photoId = phonesCursor.getLong(COLUMN_INDEX_PHOTO_ID);
mNumber = PhoneNumberUtils.formatNumber(
phonesCursor.getString(COLUMN_INDEX_NUMBER),
phonesCursor.getString(COLUMN_INDEX_NORMALIZED_NUMBER),
countryIso);
callLabel = Phone.getTypeLabel(getResources(),
phonesCursor.getInt(COLUMN_INDEX_TYPE),
phonesCursor.getString(COLUMN_INDEX_LABEL));
} else {
mNumber = PhoneNumberUtils.formatNumber(mNumber, countryIso);
}
} finally {
if (phonesCursor != null) phonesCursor.close();
}
mCallActionView.setVisibility(View.VISIBLE);
mCallActionView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent callIntent = new Intent(Intent.ACTION_CALL_PRIVILEGED,
Uri.fromParts("tel", mNumber, null));
startActivity(callIntent);
}
});
if (callLabel != null) {
numberText = FormatUtils.applyStyleToSpan(Typeface.BOLD,
callLabel + " " + mNumber, 0,
callLabel.length(),
Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
} else {
numberText = mNumber;
}
// Build list of various available actions
List<ViewEntry> actions = new ArrayList<ViewEntry>();
Intent smsIntent = new Intent(Intent.ACTION_SENDTO,
Uri.fromParts("sms", mNumber, null));
actions.add(new ViewEntry(R.drawable.sym_action_sms,
getString(R.string.menu_sendTextMessage), smsIntent));
// Let user view contact details if they exist, otherwise add option
// to create new contact from this number.
if (personUri != null) {
Intent viewIntent = new Intent(Intent.ACTION_VIEW, personUri);
actions.add(new ViewEntry(R.drawable.sym_action_view_contact,
getString(R.string.menu_viewContact), viewIntent));
} else {
Intent createIntent = new Intent(Intent.ACTION_INSERT_OR_EDIT);
createIntent.setType(Contacts.CONTENT_ITEM_TYPE);
createIntent.putExtra(Insert.PHONE, mNumber);
actions.add(new ViewEntry(R.drawable.sym_action_add,
getString(R.string.recentCalls_addToContact), createIntent));
}
ViewAdapter adapter = new ViewAdapter(this, actions);
setListAdapter(adapter);
}
mNameView.setText(nameText);
mNumberView.setText(numberText);
loadContactPhotos(photoId);
} else {
// Something went wrong reading in our primary data, so we're going to
// bail out and show error to users.
Toast.makeText(this, R.string.toast_call_detail_error,
Toast.LENGTH_SHORT).show();
finish();
}
} finally {
if (callCursor != null) {
callCursor.close();
}
}
}
/** Load the contact photos and places them in the corresponding views. */
private void loadContactPhotos(final long photoId) {
// There seem to be a limitation in the ContactPhotoManager that does not allow requesting
// two photos at once.
// TODO: Figure out the problem with ContactPhotoManager and remove this nonsense.
mContactPhotoView.post(new Runnable() {
@Override
public void run() {
mContactPhotoManager.loadPhoto(mContactPhotoView, photoId);
mContactPhotoView.postDelayed(new Runnable() {
@Override
public void run() {
mContactPhotoManager.loadPhoto(mContactBackgroundView, photoId);
}
}, 100);
}
});
}
private String formatDuration(long elapsedSeconds) {
long minutes = 0;
long seconds = 0;
if (elapsedSeconds >= 60) {
minutes = elapsedSeconds / 60;
elapsedSeconds -= minutes * 60;
}
seconds = elapsedSeconds;
return getString(R.string.callDetailsDurationFormat, minutes, seconds);
}
static final class ViewEntry {
public int icon = -1;
public String text = null;
public Intent intent = null;
public String label = null;
public String number = null;
public ViewEntry(int icon, String text, Intent intent) {
this.icon = icon;
this.text = text;
this.intent = intent;
}
}
static final class ViewAdapter extends BaseAdapter {
private final List<ViewEntry> mActions;
private final LayoutInflater mInflater;
public ViewAdapter(Context context, List<ViewEntry> actions) {
mActions = actions;
mInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
}
@Override
public int getCount() {
return mActions.size();
}
@Override
public Object getItem(int position) {
return mActions.get(position);
}
@Override
public long getItemId(int position) {
return position;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
// Make sure we have a valid convertView to start with
if (convertView == null) {
convertView = mInflater.inflate(R.layout.call_detail_list_item, parent, false);
}
// Fill action with icon and text.
ViewEntry entry = mActions.get(position);
convertView.setTag(entry);
ImageView icon = (ImageView) convertView.findViewById(R.id.icon);
TextView text = (TextView) convertView.findViewById(android.R.id.text1);
icon.setImageResource(entry.icon);
text.setText(entry.text);
View line2 = convertView.findViewById(R.id.line2);
boolean numberEmpty = TextUtils.isEmpty(entry.number);
boolean labelEmpty = TextUtils.isEmpty(entry.label) || numberEmpty;
if (labelEmpty && numberEmpty) {
line2.setVisibility(View.GONE);
} else {
line2.setVisibility(View.VISIBLE);
TextView label = (TextView) convertView.findViewById(R.id.label);
if (labelEmpty) {
label.setVisibility(View.GONE);
} else {
label.setText(entry.label);
label.setVisibility(View.VISIBLE);
}
TextView number = (TextView) convertView.findViewById(R.id.number);
number.setText(entry.number);
}
return convertView;
}
}
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
// Handle passing action off to correct handler.
if (view.getTag() instanceof ViewEntry) {
ViewEntry entry = (ViewEntry) view.getTag();
if (entry.intent != null) {
startActivity(entry.intent);
}
}
}
@Override
public void startSearch(String initialQuery, boolean selectInitialQuery, Bundle appSearchData,
boolean globalSearch) {
if (globalSearch) {
super.startSearch(initialQuery, selectInitialQuery, appSearchData, globalSearch);
} else {
ContactsSearchManager.startSearch(this, initialQuery);
}
}
}