/**
* Copyright (C) 2010-2012 Regis Montoya (aka r3gis - www.r3gis.fr)
* This file is part of CSipSimple.
*
* CSipSimple is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
* If you own a pjsip commercial license you can also redistribute it
* and/or modify it under the terms of the GNU Lesser General Public License
* as an android library.
*
* CSipSimple is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with CSipSimple. If not, see <http://www.gnu.org/licenses/>.
*/
package com.csipsimple.ui.calllog;
import android.content.ContentResolver;
import android.content.ContentUris;
import android.content.Intent;
import android.content.res.Resources;
import android.database.Cursor;
import android.net.Uri;
import android.os.Bundle;
import android.provider.CallLog;
import android.provider.CallLog.Calls;
import android.text.TextUtils;
import android.view.LayoutInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageButton;
import android.widget.ImageView;
import android.widget.ListView;
import android.widget.TextView;
import com.actionbarsherlock.app.SherlockFragment;
import com.csipsimple.R;
import com.csipsimple.api.SipManager;
import com.csipsimple.api.SipProfile;
import com.csipsimple.api.SipUri;
import com.csipsimple.models.CallerInfo;
import com.csipsimple.utils.ContactsAsyncHelper;
import com.csipsimple.utils.Log;
import com.csipsimple.utils.contacts.ContactsWrapper;
import com.csipsimple.widgets.AccountChooserButton;
/**
* Displays the details of a specific call log entry.
* <p>
* This activity can be either started with the URI of a single call log entry,
* or with the {@link #EXTRA_CALL_LOG_IDS} extra to specify a group of call log
* entries.
*/
public class CallLogDetailsFragment extends SherlockFragment {
private static final String THIS_FILE = "CallLogDetailsFragment";
/** A long array extra containing ids of call log entries to display. */
public static final String EXTRA_CALL_LOG_IDS = "EXTRA_CALL_LOG_IDS";
private PhoneCallDetailsHelper mPhoneCallDetailsHelper;
private TextView mHeaderTextView;
private View mHeaderOverlayView;
private ImageView mMainActionView, mContactBackgroundView;
private ImageButton mMainActionPushLayerView;
private AccountChooserButton mAccountChooserButton;
/* package */Resources mResources;
private LayoutInflater mInflater;
public interface OnQuitListener {
public void onQuit();
public void onShowCallLog(long[] callIds);
}
private OnQuitListener quitListener;
public void setOnQuitListener(OnQuitListener l) {
quitListener = l;
}
private static final String[] CALL_LOG_PROJECTION = new String[] {
CallLog.Calls.DATE,
CallLog.Calls.DURATION,
CallLog.Calls.NUMBER,
CallLog.Calls.TYPE,
SipManager.CALLLOG_PROFILE_ID_FIELD,
SipManager.CALLLOG_STATUS_CODE_FIELD,
SipManager.CALLLOG_STATUS_TEXT_FIELD
};
private static final int DATE_COLUMN_INDEX = 0;
private static final int DURATION_COLUMN_INDEX = 1;
private static final int NUMBER_COLUMN_INDEX = 2;
private static final int CALL_TYPE_COLUMN_INDEX = 3;
private static final int PROFILE_ID_COLUMN_INDEX = 4;
private static final int STATUS_CODE_COLUMN_INDEX = 5;
private static final int STATUS_TEXT_COLUMN_INDEX = 6;
/**
* Action when the call icon is pressed
*/
private final View.OnClickListener mPrimaryActionListener = new View.OnClickListener() {
@Override
public void onClick(View view) {
String nbr = (String) view.getTag();
if (!TextUtils.isEmpty(nbr)) {
SipProfile acc = mAccountChooserButton.getSelectedAccount();
Intent it = new Intent(Intent.ACTION_CALL);
it.setData(SipUri.forgeSipUri(SipManager.PROTOCOL_CSIP, nbr));
it.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
it.putExtra(SipProfile.FIELD_ACC_ID, acc.id);
startActivity(it);
}
}
};
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View v = inflater.inflate(R.layout.call_detail, container, false);
mResources = getResources();
mInflater = inflater;
mPhoneCallDetailsHelper = new PhoneCallDetailsHelper(mResources);
mHeaderTextView = (TextView) v.findViewById(R.id.header_text);
mHeaderOverlayView = v.findViewById(R.id.photo_text_bar);
mMainActionView = (ImageView) v.findViewById(R.id.main_action);
mMainActionPushLayerView = (ImageButton) v.findViewById(R.id.main_action_push_layer);
mContactBackgroundView = (ImageView) v.findViewById(R.id.contact_background);
mAccountChooserButton = (AccountChooserButton) v.findViewById(R.id.call_choose_account);
return v;
}
@Override
public void onResume() {
super.onResume();
updateData(getCallLogEntryUris());
}
/**
* Returns the list of URIs to show.
* <p>
* There are two ways the URIs can be provided to the activity: as the data
* on the intent, or as a list of ids in the call log added as an extra on
* the URI.
* <p>
* If both are available, the data on the intent takes precedence.
*/
private Uri[] getCallLogEntryUris() {
long[] ids = getArguments().getLongArray(EXTRA_CALL_LOG_IDS);
Uri[] uris = new Uri[ids.length];
for (int index = 0; index < ids.length; ++index) {
uris[index] = ContentUris.withAppendedId(SipManager.CALLLOG_ID_URI_BASE, ids[index]);
}
return uris;
}
/**
* Update user interface with details of given call.
*
* @param callUris URIs into {@link CallLog.Calls} of the calls to be
* displayed
*/
private void updateData(final Uri... callUris) {
final int numCalls = callUris.length;
if(numCalls == 0) {
Log.w(THIS_FILE, "No calls logs as parameters");
return;
}
PhoneCallDetails[] details = new PhoneCallDetails[numCalls];
for (int index = 0; index < numCalls; ++index) {
details[index] = getPhoneCallDetailsForUri(callUris[index]);
}
// We know that all calls are from the same number and the same contact,
// so pick the
// first.
PhoneCallDetails firstDetails = details[0];
final Uri contactUri = firstDetails.contactUri;
final Uri photoUri = firstDetails.photoUri;
// Set the details header, based on the first phone call.
mPhoneCallDetailsHelper.setCallDetailsHeader(mHeaderTextView, firstDetails);
// Cache the details about the phone number.
// final Uri numberCallUri = mPhoneNumberHelper.getCallUri(mNumber);
// final boolean canPlaceCallsTo =
// mPhoneNumberHelper.canPlaceCallsTo(mNumber);
// final boolean isVoicemailNumber =
// mPhoneNumberHelper.isVoicemailNumber(mNumber);
// final boolean isSipNumber = mPhoneNumberHelper.isSipNumber(mNumber);
// Let user view contact details if they exist, otherwise add option to
// create new
// contact from this number.
final Intent mainActionIntent;
final int mainActionIcon;
final String mainActionDescription;
final CharSequence nameOrNumber;
if (!TextUtils.isEmpty(firstDetails.name)) {
nameOrNumber = firstDetails.name;
} else {
nameOrNumber = firstDetails.number;
}
if (contactUri != null) {
mainActionIntent = new Intent(Intent.ACTION_VIEW, contactUri);
mainActionIcon = R.drawable.ic_contacts_holo_dark;
mainActionDescription = nameOrNumber.toString();
} else if(!TextUtils.isEmpty(firstDetails.number)){
mainActionIntent = ContactsWrapper.getInstance().getAddContactIntent((String) firstDetails.name, (String) firstDetails.number);
mainActionIcon = R.drawable.ic_add_contact_holo_dark;
mainActionDescription = getString(R.string.menu_add_to_contacts);
if(TextUtils.isEmpty(firstDetails.name)) {
mHeaderTextView.setText(R.string.menu_add_to_contacts);
}else {
mHeaderTextView.setText(getString(R.string.menu_add_address_to_contacts, firstDetails.name));
}
}else {
// If we cannot call the number, when we probably cannot add it as a
// contact either.
// This is usually the case of private, unknown, or payphone
// numbers.
mainActionIntent = null;
mainActionIcon = 0;
mainActionDescription = null;
}
if (mainActionIntent == null) {
mMainActionView.setVisibility(View.INVISIBLE);
mMainActionPushLayerView.setVisibility(View.GONE);
mHeaderTextView.setVisibility(View.INVISIBLE);
mHeaderOverlayView.setVisibility(View.INVISIBLE);
} else {
mMainActionView.setVisibility(View.VISIBLE);
mMainActionView.setImageResource(mainActionIcon);
mMainActionPushLayerView.setVisibility(View.VISIBLE);
mMainActionPushLayerView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
startActivity(mainActionIntent);
}
});
mMainActionPushLayerView.setContentDescription(mainActionDescription);
mHeaderTextView.setVisibility(View.VISIBLE);
mHeaderOverlayView.setVisibility(View.VISIBLE);
}
// This action allows to call the number that places the call.
// final CharSequence displayNumber = firstDetails.formattedNumber;
CharSequence displayNumber;
if (!TextUtils.isEmpty(firstDetails.number)) {
displayNumber = SipUri.getCanonicalSipContact(firstDetails.number.toString(), false);
} else {
displayNumber = mResources.getString(R.string.unknown);
}
String callText = getString(R.string.description_call, displayNumber);
configureCallButton(callText, firstDetails.numberLabel, firstDetails.number);
ListView historyList = (ListView) getView().findViewById(R.id.history);
historyList.setAdapter(new CallDetailHistoryAdapter(getActivity(), mInflater, details));
mAccountChooserButton.setTargetAccount(firstDetails.accountId);
loadContactPhotos(photoUri, contactUri);
}
/** Return the phone call details for a given call log URI. */
private PhoneCallDetails getPhoneCallDetailsForUri(Uri callUri) {
ContentResolver resolver = getActivity().getContentResolver();
Cursor callCursor = resolver.query(callUri, CALL_LOG_PROJECTION, null, null, null);
try {
if (callCursor == null || !callCursor.moveToFirst()) {
throw new IllegalArgumentException("Cannot find content: " + callUri);
}
// Read call log specifics.
String number = 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);
Long accountId = callCursor.getLong(PROFILE_ID_COLUMN_INDEX);
int statusCode = callCursor.getInt(STATUS_CODE_COLUMN_INDEX);
String statusText = callCursor.getString(STATUS_TEXT_COLUMN_INDEX);
// Formatted phone number.
final CharSequence formattedNumber;
// Read contact specifics.
final CharSequence nameText;
final int numberType;
final CharSequence numberLabel;
final Uri photoUri;
final Uri lookupUri;
// If this is not a regular number, there is no point in looking it
// up in the contacts.
CallerInfo info = CallerInfo.getCallerInfoFromSipUri(getActivity(), number);
if (info == null) {
formattedNumber = number;
nameText = "";
numberType = 0;
numberLabel = "";
photoUri = null;
lookupUri = null;
} else {
formattedNumber = info.phoneNumber;
nameText = info.name;
numberType = info.numberType;
numberLabel = info.phoneLabel;
photoUri = info.photoUri;
lookupUri = info.contactContentUri;
}
return new PhoneCallDetails(number, formattedNumber,
new int[] {
callType
}, date, duration,
accountId, statusCode, statusText,
nameText, numberType, numberLabel, lookupUri, photoUri);
} finally {
if (callCursor != null) {
callCursor.close();
}
}
}
/** Load the contact photos and places them in the corresponding views. */
private void loadContactPhotos(Uri photoUri, Uri contactUri) {
int defaultLargePhoto = R.drawable.ic_contact_picture_180_holo_dark;
if (photoUri != null) {
// Android 4.0 - high res photo
ContactsAsyncHelper.updateImageViewWithContactPhotoAsync(getActivity(),
mContactBackgroundView,
photoUri, defaultLargePhoto);
} else if (contactUri != null) {
CallerInfo person = new CallerInfo();
person.contactContentUri = contactUri;
// Android < 4.0 - low res picture
ContactsAsyncHelper.updateImageViewWithContactPhotoAsync(getActivity(),
mContactBackgroundView, person, defaultLargePhoto);
} else {
// Default picture
mContactBackgroundView.setImageResource(defaultLargePhoto);
}
}
/** Configures the call button area using the given entry. */
private void configureCallButton(String callText, CharSequence nbrLabel, CharSequence number) {
View convertView = getView().findViewById(R.id.call_and_sms);
convertView.setVisibility(TextUtils.isEmpty(number) ? View.GONE : View.VISIBLE);
TextView text = (TextView) convertView.findViewById(R.id.call_and_sms_text);
View mainAction = convertView.findViewById(R.id.call_and_sms_main_action);
mainAction.setOnClickListener(mPrimaryActionListener);
mainAction.setContentDescription(callText);
if(TextUtils.isEmpty(number)) {
number = "";
}
mainAction.setTag(SipUri.getCanonicalSipContact(number.toString(), false));
text.setText(callText);
TextView label = (TextView) convertView.findViewById(R.id.call_and_sms_label);
if (TextUtils.isEmpty(nbrLabel)) {
label.setVisibility(View.GONE);
} else {
label.setText(nbrLabel);
label.setVisibility(View.VISIBLE);
}
}
public void onMenuRemoveFromCallLog(MenuItem menuItem) {
final StringBuilder callIds = new StringBuilder();
for (Uri callUri : getCallLogEntryUris()) {
if (callIds.length() != 0) {
callIds.append(",");
}
callIds.append(ContentUris.parseId(callUri));
}
getActivity().getContentResolver().delete(SipManager.CALLLOG_URI,
Calls._ID + " IN (" + callIds + ")", null);
if (quitListener != null) {
quitListener.onQuit();
}
}
}