package com.integralblue.callerid;
import java.util.Locale;
import roboguice.fragment.RoboListFragment;
import roboguice.inject.InjectResource;
import roboguice.util.Ln;
import roboguice.util.RoboAsyncTask;
import android.content.Context;
import android.content.Intent;
import android.database.Cursor;
import android.graphics.drawable.Drawable;
import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
import android.provider.CallLog.Calls;
import android.support.v4.app.LoaderManager;
import android.support.v4.content.CursorLoader;
import android.support.v4.content.Loader;
import android.support.v4.widget.CursorAdapter;
import android.support.v4.widget.ResourceCursorAdapter;
import android.telephony.PhoneNumberUtils;
import android.text.TextUtils;
import android.text.format.DateUtils;
import android.view.View;
import android.widget.ImageView;
import android.widget.ListView;
import android.widget.TextView;
import com.google.i18n.phonenumbers.NumberParseException;
import com.google.i18n.phonenumbers.PhoneNumberUtil;
import com.google.i18n.phonenumbers.Phonenumber.PhoneNumber;
import com.google.i18n.phonenumbers.geocoding.PhoneNumberOfflineGeocoder;
import com.google.inject.Inject;
import com.integralblue.callerid.CallerIDLookup.NoResultException;
import com.integralblue.callerid.contacts.ContactsHelper;
import com.integralblue.callerid.inject.CountryDetector;
public class RecentCallsFragment extends RoboListFragment {
@Inject ContactsHelper contactsHelper;
@Inject CallerIDLookup callerIDLookup;
@Inject
CountryDetector countryDetector;
@InjectResource(R.drawable.ic_call_log_list_incoming_call)
Drawable drawableIncoming;
@InjectResource(R.drawable.ic_call_log_list_outgoing_call)
Drawable drawableOutgoing;
@InjectResource(R.drawable.ic_call_log_list_missed_call)
Drawable drawableMissed;
@InjectResource(R.string.lookup_no_result)
String lookupNoResult;
@InjectResource(R.string.lookup_error)
String lookupError;
@InjectResource(R.string.lookup_in_progress)
String lookupInProgress;
@InjectResource(R.string.lookup_payphone)
String lookupPayphone;
@InjectResource(R.string.lookup_private)
String lookupPrivate;
@InjectResource(R.string.lookup_unknown)
String lookupUnknown;
private static final int CALL_LOG_LOADER = 1;
static final int ID_COLUMN_INDEX = 0;
static final int NUMBER_COLUMN_INDEX = 1;
static final int DATE_COLUMN_INDEX = 2;
static final int DURATION_COLUMN_INDEX = 3;
static final int CALL_TYPE_COLUMN_INDEX = 4;
static final int CALLER_NAME_COLUMN_INDEX = 5;
static final int CALLER_NUMBERTYPE_COLUMN_INDEX = 6;
static final int CALLER_NUMBERLABEL_COLUMN_INDEX = 7;
static final String[] CALL_LOG_PROJECTION = new String[] {
Calls._ID,
Calls.NUMBER,
Calls.DATE,
Calls.DURATION,
Calls.TYPE,
Calls.CACHED_NAME,
Calls.CACHED_NUMBER_TYPE,
Calls.CACHED_NUMBER_LABEL
};
final String ORDER = android.provider.CallLog.Calls.DATE + " DESC";
//keep track of if we've prompted them already. We only want to prompt the user once per run of the application.
boolean promptedForNewVersion = false;
class RecentCallsLookupAsyncTask extends RoboAsyncTask<CallerIDResult> {
final String phoneNumber;
final View view;
final View callIcon;
final ImageView callTypeIcon;
final TextView dateView;
final TextView labelView;
final TextView numberView;
final TextView line1View;
final int callType;
final long date;
String offlineGeocoderResult = null;
public RecentCallsLookupAsyncTask(Context context, View view, String phoneNumber, long date, int callType) {
super(context, new Handler(context.getMainLooper()));
this.phoneNumber = phoneNumber;
this.view = view;
callIcon = (View) view.findViewById(R.id.call_icon);
callTypeIcon = (ImageView) view.findViewById(R.id.call_type_icon);
dateView = (TextView) view.findViewById(R.id.date);
labelView = (TextView) view.findViewById(R.id.label);
numberView = (TextView) view.findViewById(R.id.number);
line1View = (TextView) view.findViewById(R.id.line1);
this.date = date;
this.callType = callType;
}
@Override
protected void onSuccess(CallerIDResult result)
throws Exception {
super.onSuccess(result);
line1View.setText(result.getName());
labelView.setText("");
}
@Override
public void execute() {
// Overriding executive seems weird... here's why I do it:
// The background task (and its related preExecute/success/... handlers)
// should only be called when a background lookup is necessary.
// In the case of "special" numbers (payphone, unknown, and private)
// any further lookup is unnecessary and not desired, but other
// setup (like displaying the right icon and right text) is.
// By overriding execute() and only calling super.execute() when
// a background lookup should be done, that goal can be achieved.
// Set the date/time field by mixing relative and absolute times.
dateView.setText(
DateUtils.getRelativeTimeSpanString(
date,
System.currentTimeMillis(),
DateUtils.MINUTE_IN_MILLIS,
DateUtils.FORMAT_ABBREV_RELATIVE));
// Set the icon
switch (callType) {
case Calls.INCOMING_TYPE:
callTypeIcon.setImageDrawable(drawableIncoming);
break;
case Calls.OUTGOING_TYPE:
callTypeIcon.setImageDrawable(drawableOutgoing);
break;
case Calls.MISSED_TYPE:
callTypeIcon.setImageDrawable(drawableMissed);
break;
}
if(TextUtils.isEmpty(phoneNumber) || SpecialPhoneNumbers.UNKNOWN_NUMBER.equals(phoneNumber)){
callIcon.setOnClickListener(null);
numberView.setText("");
line1View.setText(lookupUnknown);
labelView.setText("");
}else if(SpecialPhoneNumbers.PRIVATE_NUMBER.equals(phoneNumber)){
callIcon.setOnClickListener(null);
numberView.setText("");
line1View.setText(lookupPrivate);
labelView.setText("");
}else if(SpecialPhoneNumbers.PAYPHONE_NUMBER.equals(phoneNumber)){
callIcon.setOnClickListener(null);
numberView.setText("");
line1View.setText(lookupPayphone);
labelView.setText("");
}else{
numberView.setText(PhoneNumberUtils.formatNumber(phoneNumber));
final PhoneNumberUtil phoneNumberUtil = PhoneNumberUtil.getInstance();
try{
final PhoneNumber phoneNumberPhoneNumber = phoneNumberUtil.parse(phoneNumber.toString(), countryDetector.getCountry());
final PhoneNumberOfflineGeocoder phoneNumberOfflineGeocoder = PhoneNumberOfflineGeocoder.getInstance();
offlineGeocoderResult = phoneNumberOfflineGeocoder.getDescriptionForNumber(phoneNumberPhoneNumber, Locale.getDefault());
}catch(NumberParseException e){
//ignore this exception
}
if("".equals(offlineGeocoderResult)) offlineGeocoderResult = null;
if(offlineGeocoderResult == null){
labelView.setText(lookupInProgress);
line1View.setText("");
}else{
line1View.setText(offlineGeocoderResult);
labelView.setText("");
}
callIcon.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
Uri callUri;
if (CallerIDApplication.isUriNumber(phoneNumber)) {
callUri = Uri.fromParts("sip", phoneNumber, null);
} else {
callUri = Uri.fromParts("tel", phoneNumber, null);
}
startActivity(new Intent(Intent.ACTION_CALL,callUri));
}
});
super.execute();
}
}
@Override
protected void onException(Exception e) throws RuntimeException {
if (e instanceof CallerIDLookup.NoResultException) {
if(offlineGeocoderResult == null){
line1View.setText(lookupNoResult);
}else{
// We're already displaying the offline geolocation results... so just leave that there.
}
} else {
Ln.e(e);
if(offlineGeocoderResult == null){
line1View.setText(lookupError);
}else{
// We're already displaying the offline geolocation results... so just leave that there.
}
}
labelView.setText("");
}
public CallerIDResult call() throws Exception {
CallerIDResult result;
try{
result = contactsHelper.getContact(phoneNumber.toString());
}catch(NoResultException e){
// not a phone number in the local contacts database, so time to query the web service
result = callerIDLookup.lookup(phoneNumber);
}
return result;
}
}
@Override
public void onViewCreated(View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
final ResourceCursorAdapter adapter = new ResourceCursorAdapter(getActivity().getApplicationContext(),R.layout.recent_calls_list_item, null, CursorAdapter.FLAG_REGISTER_CONTENT_OBSERVER) {
@Override
public void bindView(View view, Context context, Cursor cursor) {
new RecentCallsLookupAsyncTask(
getActivity(),
view,
cursor.getString(NUMBER_COLUMN_INDEX),
cursor.getLong(DATE_COLUMN_INDEX),
cursor.getInt(CALL_TYPE_COLUMN_INDEX)).execute();
}
};
setListAdapter(adapter);
getLoaderManager().initLoader(CALL_LOG_LOADER, null, new LoaderManager.LoaderCallbacks<Cursor>(){
public Loader<Cursor> onCreateLoader(int id, Bundle args) {
return new CursorLoader(getActivity(),
android.provider.CallLog.Calls.CONTENT_URI, CALL_LOG_PROJECTION, null, null, ORDER);
}
public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
adapter.swapCursor(data);
}
public void onLoaderReset(Loader<Cursor> loader) {
adapter.swapCursor(null);
}
});
}
@Override
public void onListItemClick(ListView l, View v, int position, long id) {
// TODO Auto-generated method stub
super.onListItemClick(l, v, position, id);
final Cursor cursor = (Cursor) getListAdapter().getItem(position);
final Intent lookupIntent = new Intent(getActivity().getApplicationContext(), MainActivity.class);
lookupIntent.putExtra("phoneNumber", cursor.getString(NUMBER_COLUMN_INDEX));
startActivity(lookupIntent);
}
}