实例介绍
【实例简介】
【实例截图】
【实例截图】
【核心代码】
/**
* Displays a list of call log entries.
*/
public class RecentListActivity extends ListActivity
implements View.OnCreateContextMenuListener {
private static final String TAG = "RecentListActivity";
/** The projection to use when querying the call log table */
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
};
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 int MENU_ITEM_BLACKLIST = 666;
/** The projection to use when querying the phones table */
static final String[] PHONES_PROJECTION = new String[] {
PhoneLookup._ID,
PhoneLookup.DISPLAY_NAME,
PhoneLookup.TYPE,
PhoneLookup.LABEL,
PhoneLookup.NUMBER,
//Wysie: Contact pictures
PhoneLookup.PHOTO_ID,
PhoneLookup.LOOKUP_KEY
};
static final int PERSON_ID_COLUMN_INDEX = 0;
static final int NAME_COLUMN_INDEX = 1;
static final int PHONE_TYPE_COLUMN_INDEX = 2;
static final int LABEL_COLUMN_INDEX = 3;
static final int MATCHED_NUMBER_COLUMN_INDEX = 4;
//Wysie: Contact pictures
static final int PHOTO_ID_COLUMN_INDEX = 5;
static final int LOOKUP_KEY_COLUMN_INDEX = 6;
private static final int MENU_ITEM_CLEAR_CALL_LOG = 1;
private static final int MENU_PREFERENCES = 2;
private static final int MENU_ITEM_CLEAR_ALL = 3;
private static final int MENU_ITEM_CLEAR_INCOMING = 4;
private static final int MENU_ITEM_CLEAR_OUTGOING = 5;
private static final int MENU_ITEM_CLEAR_MISSED = 6;
private static final int MENU_ITEM_DELETE = 7;
private static final int QUERY_TOKEN = 53;
private static final int UPDATE_TOKEN = 54;
private static final int DIALOG_CONFIRM_DELETE_ALL = 1;
RecentCallsAdapter mAdapter;
private QueryHandler mQueryHandler;
String mVoiceMailNumber;
//Wysie
private MenuItem mPreferences;
private SharedPreferences ePrefs;
private static boolean exactTime;
private static boolean is24hour;
private static boolean showSeconds;
private static final String format24HourSeconds = "MMM d, kk:mm:ss";
private static final String format24Hour = "MMM d, kk:mm";
private static final String format12HourSeconds = "MMM d, h:mm:ssaa";
private static final String format12Hour = "MMM d, h:mmaa";
private static int mRecordCount = 0;
//Wysie: Contact pictures
private static ExecutorService sImageFetchThreadPool;
private static boolean mDisplayPhotos;
private static boolean isQuickContact = true;
private static boolean showDialButton = true;
private static final String INSERT_BLACKLIST = "com.android.phone.INSERT_BLACKLIST";
private ContactPhotoLoader mPhotoLoader;
static final class ContactInfo {
public long personId;
public String name;
public int type;
public String label;
public String number;
public String formattedNumber;
//Wysie: Contact pictures
public long photoId;
public String lookupKey;
public static ContactInfo EMPTY = new ContactInfo();
}
public static final class RecentCallsListItemViews {
//Wysie: Contact pictures
QuickContactBadge photoView;
ImageView nonQuickContactPhotoView;
TextView line1View;
TextView labelView;
TextView numberView;
TextView dateView;
ImageView iconView;
View callView;
ImageView groupIndicator;
TextView groupSize;
View dividerView;
}
static final class CallerInfoQuery {
String number;
int position;
String name;
int numberType;
String numberLabel;
}
static final class CallerInfo{
static int UNKNOWN_NUMBER;
static int PRIVATE_NUMBER;
static int PAYPHONE_NUMBER;
}
/**
* Shared builder used by {@link #formatPhoneNumber(String)} to minimize
* allocations when formatting phone numbers.
*/
private static final SpannableStringBuilder sEditable = new SpannableStringBuilder();
/**
* Invalid formatting type constant for {@link #sFormattingType}.
*/
private static final int FORMATTING_TYPE_INVALID = -1;
/**
* Cached formatting type for current {@link Locale}, as provided by
* {@link PhoneNumberUtils#getFormatTypeForLocale(Locale)}.
*/
private static int sFormattingType = FORMATTING_TYPE_INVALID;
//Wysie: Contact pictures
final static class PhotoInfo {
public int position;
public long photoId;
public Uri contactUri;
public PhotoInfo(int position, long photoId, Uri contactUri) {
this.position = position;
this.photoId = photoId;
this.contactUri = contactUri;
}
public QuickContactBadge photoView;
}
/** Adapter class to fill in data for the Call Log */
final class RecentCallsAdapter extends GroupingListAdapter
implements Runnable, ViewTreeObserver.OnPreDrawListener, View.OnClickListener, OnScrollListener {
HashMap<String,ContactInfo> mContactInfo;
private final LinkedList<CallerInfoQuery> mRequests;
private volatile boolean mDone;
private boolean mLoading = true;
ViewTreeObserver.OnPreDrawListener mPreDrawListener;
private static final int REDRAW = 1;
private static final int START_THREAD = 2;
private boolean mFirst;
private Thread mCallerIdThread;
private CharSequence[] mLabelArray;
private Drawable mDrawableIncoming;
private Drawable mDrawableOutgoing;
private Drawable mDrawableMissed;
/**
* Reusable char array buffers.
*/
private CharArrayBuffer mBuffer1 = new CharArrayBuffer(128);
private CharArrayBuffer mBuffer2 = new CharArrayBuffer(128);
public void onClick(View view) {
if (view instanceof QuickContactBadge) {
PhotoInfo info = (PhotoInfo)view.getTag();
QuickContact.showQuickContact(mContext, view, info.contactUri, QuickContact.MODE_MEDIUM, null);
isQuickContact = true;
}else {
String number = (String) view.getTag();
if (!TextUtils.isEmpty(number)) {
Uri telUri = Uri.fromParts("tel", number, null);
startActivity(new Intent(Intent.ACTION_CALL, telUri));
}
}
}
public boolean onPreDraw() {
if (mFirst) {
mHandler.sendEmptyMessageDelayed(START_THREAD, 1000);
mFirst = false;
}
return true;
}
private Handler mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case REDRAW:
notifyDataSetChanged();
break;
case START_THREAD:
startRequestProcessing();
break;
}
}
};
public RecentCallsAdapter() {
super(RecentListActivity.this);
mContactInfo = new HashMap<String,ContactInfo>();
mRequests = new LinkedList<CallerInfoQuery>();
mPreDrawListener = null;
mDrawableIncoming = getResources().getDrawable(
R.drawable.ic_call_log_list_incoming_call);
mDrawableOutgoing = getResources().getDrawable(
R.drawable.ic_call_log_list_outgoing_call);
mDrawableMissed = getResources().getDrawable(
R.drawable.ic_call_log_list_missed_call);
//mLabelArray = getResources().getTextArray(com.android.internal.R.array.phoneTypes);
}
/**
* Requery on background thread when {@link Cursor} changes.
*/
@Override
protected void onContentChanged() {
// Start async requery
startQuery();
}
void setLoading(boolean loading) {
mLoading = loading;
}
@Override
public boolean isEmpty() {
if (mLoading) {
// We don't want the empty state to show when loading.
return false;
} else {
return super.isEmpty();
}
}
public ContactInfo getContactInfo(String number) {
return mContactInfo.get(number);
}
public void startRequestProcessing() {
mDone = false;
mCallerIdThread = new Thread(this);
mCallerIdThread.setPriority(Thread.MIN_PRIORITY);
mCallerIdThread.start();
}
public void stopRequestProcessing() {
mDone = true;
if (mCallerIdThread != null) mCallerIdThread.interrupt();
}
public void clearCache() {
synchronized (mContactInfo) {
mContactInfo.clear();
}
}
private void updateCallLog(CallerInfoQuery ciq, ContactInfo ci) {
// Check if they are different. If not, don't update.
if (TextUtils.equals(ciq.name, ci.name)
&& TextUtils.equals(ciq.numberLabel, ci.label)
&& ciq.numberType == ci.type) {
return;
}
ContentValues values = new ContentValues(3);
values.put(Calls.CACHED_NAME, ci.name);
values.put(Calls.CACHED_NUMBER_TYPE, ci.type);
values.put(Calls.CACHED_NUMBER_LABEL, ci.label);
try {
RecentListActivity.this.getContentResolver().update(Calls.CONTENT_URI, values,
Calls.NUMBER "='" ciq.number "'", null);
} catch (SQLiteDiskIOException e) {
Log.w(TAG, "Exception while updating call info", e);
} catch (SQLiteFullException e) {
Log.w(TAG, "Exception while updating call info", e);
} catch (SQLiteDatabaseCorruptException e) {
Log.w(TAG, "Exception while updating call info", e);
}
}
private void enqueueRequest(String number, int position,
String name, int numberType, String numberLabel) {
CallerInfoQuery ciq = new CallerInfoQuery();
ciq.number = number;
ciq.position = position;
ciq.name = name;
ciq.numberType = numberType;
ciq.numberLabel = numberLabel;
synchronized (mRequests) {
mRequests.add(ciq);
mRequests.notifyAll();
}
}
private boolean queryContactInfo(CallerInfoQuery ciq) {
// First check if there was a prior request for the same number
// that was already satisfied
ContactInfo info = mContactInfo.get(ciq.number);
boolean needNotify = false;
if (info != null && info != ContactInfo.EMPTY) {
return true;
} else {
Cursor phonesCursor =
RecentListActivity.this.getContentResolver().query(
Uri.withAppendedPath(PhoneLookup.CONTENT_FILTER_URI,
Uri.encode(ciq.number)),
PHONES_PROJECTION, null, null, null);
if (phonesCursor != null) {
if (phonesCursor.moveToFirst()) {
info = new ContactInfo();
info.personId = phonesCursor.getLong(PERSON_ID_COLUMN_INDEX);
info.name = phonesCursor.getString(NAME_COLUMN_INDEX);
info.type = phonesCursor.getInt(PHONE_TYPE_COLUMN_INDEX);
info.label = phonesCursor.getString(LABEL_COLUMN_INDEX);
info.number = phonesCursor.getString(MATCHED_NUMBER_COLUMN_INDEX);
//Wysie: Contact pictures
info.photoId = phonesCursor.getLong(PHOTO_ID_COLUMN_INDEX);
info.lookupKey = phonesCursor.getString(LOOKUP_KEY_COLUMN_INDEX);
// New incoming phone number invalidates our formatted
// cache. Any cache fills happen only on the GUI thread.
info.formattedNumber = null;
mContactInfo.put(ciq.number, info);
// Inform list to update this item, if in view
needNotify = true;
}
phonesCursor.close();
}
}
if (info != null) {
updateCallLog(ciq, info);
}
return needNotify;
}
/*
* Handles requests for contact name and number type
* run()
*/
public void run() {
boolean needNotify = false;
while (!mDone) {
CallerInfoQuery ciq = null;
synchronized (mRequests) {
if (!mRequests.isEmpty()) {
ciq = mRequests.removeFirst();
} else {
if (needNotify) {
needNotify = false;
mHandler.sendEmptyMessage(REDRAW);
}
try {
mRequests.wait(1000);
} catch (InterruptedException ie) {
// Ignore and continue processing requests
}
}
}
if (ciq != null && queryContactInfo(ciq)) {
needNotify = true;
}
}
}
@Override
protected void addGroups(Cursor cursor) {
int count = cursor.getCount();
if (count == 0) {
return;
}
int groupItemCount = 1;
CharArrayBuffer currentValue = mBuffer1;
CharArrayBuffer value = mBuffer2;
cursor.moveToFirst();
cursor.copyStringToBuffer(NUMBER_COLUMN_INDEX, currentValue);
int currentCallType = cursor.getInt(CALL_TYPE_COLUMN_INDEX);
for (int i = 1; i < count; i ) {
cursor.moveToNext();
cursor.copyStringToBuffer(NUMBER_COLUMN_INDEX, value);
boolean sameNumber = equalPhoneNumbers(value, currentValue);
// Group adjacent calls with the same number. Make an exception
// for the latest item if it was a missed call. We don't want
// a missed call to be hidden inside a group.
if (sameNumber && currentCallType != Calls.MISSED_TYPE) {
groupItemCount ;
} else {
if (groupItemCount > 1) {
addGroup(i - groupItemCount, groupItemCount, false);
}
groupItemCount = 1;
// Swap buffers
CharArrayBuffer temp = currentValue;
currentValue = value;
value = temp;
// If we have just examined a row following a missed call, make
// sure that it is grouped with subsequent calls from the same number
// even if it was also missed.
if (sameNumber && currentCallType == Calls.MISSED_TYPE) {
currentCallType = 0; // "not a missed call"
} else {
currentCallType = cursor.getInt(CALL_TYPE_COLUMN_INDEX);
}
}
}
if (groupItemCount > 1) {
addGroup(count - groupItemCount, groupItemCount, false);
}
}
protected boolean equalPhoneNumbers(CharArrayBuffer buffer1, CharArrayBuffer buffer2) {
// TODO add PhoneNumberUtils.compare(CharSequence, CharSequence) to avoid
// string allocation
return PhoneNumberUtils.compare(new String(buffer1.data, 0, buffer1.sizeCopied),
new String(buffer2.data, 0, buffer2.sizeCopied));
}
@Override
protected View newStandAloneView(Context context, ViewGroup parent) {
LayoutInflater inflater =
(LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
View view = inflater.inflate(R.layout.recent_calls_list_item, parent, false);
findAndCacheViews(view);
return view;
}
@Override
protected void bindStandAloneView(View view, Context context, Cursor cursor) {
bindView(context, view, cursor);
}
@Override
protected View newChildView(Context context, ViewGroup parent) {
LayoutInflater inflater =
(LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
View view = inflater.inflate(R.layout.recent_calls_list_child_item, parent, false);
findAndCacheViews(view);
return view;
}
@Override
protected void bindChildView(View view, Context context, Cursor cursor) {
bindView(context, view, cursor);
}
@Override
protected View newGroupView(Context context, ViewGroup parent) {
LayoutInflater inflater =
(LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
View view = inflater.inflate(R.layout.recent_calls_list_group_item, parent, false);
findAndCacheViews(view);
return view;
}
@Override
protected void bindGroupView(View view, Context context, Cursor cursor, int groupSize,
boolean expanded) {
final RecentCallsListItemViews views = (RecentCallsListItemViews) view.getTag();
/* int groupIndicator = expanded
? com.android.internal.R.drawable.expander_ic_maximized
: com.android.internal.R.drawable.expander_ic_minimized;*/
//views.groupIndicator.setImageResource(groupIndicator);
views.groupSize.setText("(" groupSize ")");
bindView(context, view, cursor);
}
private void findAndCacheViews(View view) {
// Get the views to bind to
RecentCallsListItemViews views = new RecentCallsListItemViews();
views.line1View = (TextView) view.findViewById(R.id.line1);
views.labelView = (TextView) view.findViewById(R.id.label);
views.numberView = (TextView) view.findViewById(R.id.number);
views.dateView = (TextView) view.findViewById(R.id.date);
views.iconView = (ImageView) view.findViewById(R.id.call_type_icon);
views.dividerView = view.findViewById(R.id.divider);
views.callView = view.findViewById(R.id.call_icon);
views.callView.setOnClickListener(this);
views.groupIndicator = (ImageView) view.findViewById(R.id.groupIndicator);
views.groupSize = (TextView) view.findViewById(R.id.groupSize);
//Wysie: Contact pictures
views.photoView = (QuickContactBadge) view.findViewById(R.id.photo);
//views.photoView.setOnClickListener(this);
views.nonQuickContactPhotoView = (ImageView) view.findViewById(R.id.noQuickContactPhoto);
view.setTag(views);
}
public void bindView(Context context, View view, Cursor c) {
final RecentCallsListItemViews views = (RecentCallsListItemViews) view.getTag();
String number = c.getString(NUMBER_COLUMN_INDEX);
String formattedNumber = null;
String callerName = c.getString(CALLER_NAME_COLUMN_INDEX);
int callerNumberType = c.getInt(CALLER_NUMBERTYPE_COLUMN_INDEX);
String callerNumberLabel = c.getString(CALLER_NUMBERLABEL_COLUMN_INDEX);
//Wysie
boolean noContactInfo = false;
// Store away the number so we can call it directly if you click on the call icon
views.callView.setTag(number);
//pateo--------------------------------------------------------------------------
views.iconView.setTag(number);
views.iconView.setOnClickListener(this);
views.iconView.setBackgroundResource(R.drawable.call_background);
//pateo
//Wysie: Use iconView to dial out if dial button is hidden
/* if (!showDialButton) {
views.iconView.setTag(number);
views.iconView.setOnClickListener(this);
views.iconView.setBackgroundResource(R.drawable.call_background);
} else {
views.iconView.setTag(null);
views.iconView.setOnClickListener(null);
//views.iconView.setBackgroundResource(0);
}*/
// Lookup contacts with this number
ContactInfo info = mContactInfo.get(number);
if (info == null) {
// Mark it as empty and queue up a request to find the name
// The db request should happen on a non-UI thread
info = ContactInfo.EMPTY;
mContactInfo.put(number, info);
enqueueRequest(number, c.getPosition(),
callerName, callerNumberType, callerNumberLabel);
} else if (info != ContactInfo.EMPTY) { // Has been queried
// Check if any data is different from the data cached in the
// calls db. If so, queue the request so that we can update
// the calls db.
if (!TextUtils.equals(info.name, callerName)
|| info.type != callerNumberType
|| !TextUtils.equals(info.label, callerNumberLabel)) {
// Something is amiss, so sync up.
enqueueRequest(number, c.getPosition(),
callerName, callerNumberType, callerNumberLabel);
}
// Format and cache phone number for found contact
if (info.formattedNumber == null) {
info.formattedNumber = formatPhoneNumber(info.number);
}
formattedNumber = info.formattedNumber;
}
String name = info.name;
int ntype = info.type;
String label = info.label;
// If there's no name cached in our hashmap, but there's one in the
// calls db, use the one in the calls db. Otherwise the name in our
// hashmap is more recent, so it has precedence.
if (TextUtils.isEmpty(name) && !TextUtils.isEmpty(callerName)) {
name = callerName;
ntype = callerNumberType;
label = callerNumberLabel;
// Format the cached call_log phone number
formattedNumber = formatPhoneNumber(number);
}
// Set the text lines and call icon.
// Assumes the call back feature is on most of the
// time. For private and unknown numbers: hide it.
views.callView.setVisibility(View.VISIBLE);
if (!TextUtils.isEmpty(name)) {
views.line1View.setText(name);
views.labelView.setVisibility(View.VISIBLE);
CharSequence numberLabel = Phone.getTypeLabel(context.getResources(), ntype, label);
views.numberView.setVisibility(View.VISIBLE);
views.numberView.setText(formattedNumber);
if (!TextUtils.isEmpty(numberLabel)) {
views.labelView.setText(numberLabel);
views.labelView.setVisibility(View.VISIBLE);
} else {
views.labelView.setVisibility(View.GONE);
}
} else {
if (number.equals(CallerInfo.UNKNOWN_NUMBER)) {
number = getString(R.string.unknown);
views.callView.setVisibility(View.INVISIBLE);
} else if (number.equals(CallerInfo.PRIVATE_NUMBER)) {
number = getString(R.string.private_num);
views.callView.setVisibility(View.INVISIBLE);
} else if (number.equals(CallerInfo.PAYPHONE_NUMBER)) {
number = getString(R.string.payphone);
} else if (PhoneNumberUtils.extractNetworkPortion(number)
.equals(mVoiceMailNumber)) {
number = getString(R.string.voicemail);
} else {
// Just a raw number, and no cache, so format it nicely
number = formatPhoneNumber(number);
}
//Wysie
noContactInfo = true;
views.line1View.setText(number);
views.numberView.setVisibility(View.GONE);
views.labelView.setVisibility(View.GONE);
}
//Wysie: Contact pictures
if (mDisplayPhotos) {
long photoId = info.photoId;
// Build soft lookup reference
final long contactId = info.personId;
final String lookupKey = info.lookupKey;
Uri contactUri = Contacts.getLookupUri(contactId, lookupKey);
ImageView viewToUse;
if (noContactInfo) {
viewToUse = views.nonQuickContactPhotoView;
views.photoView.setVisibility(View.INVISIBLE);
views.nonQuickContactPhotoView.setVisibility(View.VISIBLE);
} else {
viewToUse = views.photoView;
//views.photoView.assignContactUri(contactUri);
//Wysie: Commented out, we handle it explicityly in onClick()
views.photoView.setTag(contactUri);
views.photoView.setVisibility(View.VISIBLE);
views.nonQuickContactPhotoView.setVisibility(View.INVISIBLE);
}
final int position = c.getPosition();
viewToUse.setTag(new PhotoInfo(position, photoId, contactUri));
mPhotoLoader.loadPhoto(viewToUse, photoId);
}
else {
views.photoView.setVisibility(View.GONE);
views.nonQuickContactPhotoView.setVisibility(View.GONE);
}
long date = c.getLong(DATE_COLUMN_INDEX);
if (!exactTime) {
// Set the date/time field by mixing relative and absolute times.
int flags = DateUtils.FORMAT_ABBREV_RELATIVE;
views.dateView.setText(
DateUtils.getRelativeTimeSpanString(date,
System.currentTimeMillis(), DateUtils.MINUTE_IN_MILLIS,
flags));
} else {
String format = null;
if (is24hour) {
if (showSeconds) {
format = format24HourSeconds;
} else {
format = format24Hour;
}
} else {
if (showSeconds) {
format = format12HourSeconds;
} else {
format = format12Hour;
}
}
views.dateView.setText(DateFormat.format(format, date));
}
views.dividerView.setVisibility(View.VISIBLE);
views.callView.setVisibility(View.VISIBLE);
//pateo
/* if (showDialButton) {
views.dividerView.setVisibility(View.VISIBLE);
views.callView.setVisibility(View.VISIBLE);
} else {
views.dividerView.setVisibility(View.GONE);
views.callView.setVisibility(View.GONE);
}*/
if (views.iconView != null) {
int type = c.getInt(CALL_TYPE_COLUMN_INDEX);
// Set the icon
switch (type) {
case Calls.INCOMING_TYPE:
views.iconView.setImageDrawable(mDrawableIncoming);
break;
case Calls.OUTGOING_TYPE:
views.iconView.setImageDrawable(mDrawableOutgoing);
break;
case Calls.MISSED_TYPE:
views.iconView.setImageDrawable(mDrawableMissed);
break;
}
}
// Listen for the first draw
if (mPreDrawListener == null) {
mFirst = true;
mPreDrawListener = this;
view.getViewTreeObserver().addOnPreDrawListener(this);
}
}
//Wysie: Contact pictures
public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount,
int totalItemCount) {
// no op
}
public void onScrollStateChanged(AbsListView view, int scrollState) {
if (scrollState == OnScrollListener.SCROLL_STATE_FLING) {
mPhotoLoader.pause();
} else if (mDisplayPhotos) {
mPhotoLoader.resume();
}
}
}
private static final class QueryHandler extends AsyncQueryHandler {
private final WeakReference<RecentListActivity> mActivity;
/**
* Simple handler that wraps background calls to catch
* {@link SQLiteException}, such as when the disk is full.
*/
protected class CatchingWorkerHandler extends AsyncQueryHandler.WorkerHandler {
public CatchingWorkerHandler(Looper looper) {
super(looper);
}
@Override
public void handleMessage(Message msg) {
try {
// Perform same query while catching any exceptions
super.handleMessage(msg);
} catch (SQLiteDiskIOException e) {
Log.w(TAG, "Exception on background worker thread", e);
} catch (SQLiteFullException e) {
Log.w(TAG, "Exception on background worker thread", e);
} catch (SQLiteDatabaseCorruptException e) {
Log.w(TAG, "Exception on background worker thread", e);
}
}
}
@Override
protected Handler createHandler(Looper looper) {
// Provide our special handler that catches exceptions
return new CatchingWorkerHandler(looper);
}
public QueryHandler(Context context) {
super(context.getContentResolver());
mActivity = new WeakReference<RecentListActivity>(
(RecentListActivity) context);
}
@Override
protected void onQueryComplete(int token, Object cookie, Cursor cursor) {
final RecentListActivity activity = mActivity.get();
if (activity != null && !activity.isFinishing()) {
final RecentListActivity.RecentCallsAdapter callsAdapter = activity.mAdapter;
callsAdapter.setLoading(false);
callsAdapter.changeCursor(cursor);
mRecordCount = cursor.getCount();
} else {
cursor.close();
}
}
}
@Override
protected void onCreate(Bundle state) {
super.onCreate(state);
//Wysie
ePrefs = PreferenceManager.getDefaultSharedPreferences(getBaseContext());
isQuickContact = false;
setContentView(R.layout.recent_calls);
mPhotoLoader = new ContactPhotoLoader(this, R.drawable.ic_contact_list_picture);
// Typing here goes to the dialer
setDefaultKeyMode(DEFAULT_KEYS_DIALER);
mAdapter = new RecentCallsAdapter();
//pateo
// getListView().setOnCreateContextMenuListener(this);
setListAdapter(mAdapter);
mVoiceMailNumber = ((TelephonyManager)getSystemService(Context.TELEPHONY_SERVICE))
.getVoiceMailNumber();
mQueryHandler = new QueryHandler(this);
// Reset locale-based formatting cache
sFormattingType = FORMATTING_TYPE_INVALID;
}
@Override
protected void onResume() {
if (isQuickContact) {
isQuickContact = false;
super.onResume();
} else {
// The adapter caches looked up numbers, clear it so they will get
// looked up again.
if (mAdapter != null) {
mAdapter.clearCache();
}
exactTime = ePrefs.getBoolean("cl_exact_time", true);
is24hour = DateFormat.is24HourFormat(this);
showSeconds = ePrefs.getBoolean("cl_show_seconds", true);
mDisplayPhotos = ePrefs.getBoolean("cl_show_pic", true);
showDialButton = ePrefs.getBoolean("cl_show_dial_button", false);
super.onResume();
startQuery();
resetNewCallsFlag();
mPhotoLoader.resume();
mAdapter.mPreDrawListener = null; // Let it restart the thread after next draw
}
}
@Override
protected void onPause() {
super.onPause();
// Kill the requests thread
mAdapter.stopRequestProcessing();
}
@Override
protected void onDestroy() {
super.onDestroy();
mPhotoLoader.stop();
mAdapter.stopRequestProcessing();
mAdapter.changeCursor(null);
}
@Override
public void onWindowFocusChanged(boolean hasFocus) {
super.onWindowFocusChanged(hasFocus);
// Clear notifications only when window gains focus. This activity won't
// immediately receive focus if the keyguard screen is above it.
if (hasFocus) {
try {
//ITelephony iTelephony = ITelephony.Stub.asInterface(ServiceManager.getService("phone"));
// 鍒濆鍖杋Telephony
Class<TelephonyManager> c = TelephonyManager.class;
TelephonyManager mTelephonyMgr = (TelephonyManager) this.getSystemService(Context.TELEPHONY_SERVICE);
TelephonyManager tManager = (TelephonyManager) this.getSystemService(Context.TELEPHONY_SERVICE);
Method getITelephonyMethod = getITelephonyMethod = c.getDeclaredMethod("getITelephony",(Class[]) null);
getITelephonyMethod.setAccessible(true);
ITelephony iTelephony = (ITelephony)getITelephonyMethod.invoke(tManager, (Object[]) null);
if (iTelephony != null) {
iTelephony.cancelMissedCallsNotification();
} else {
Log.w(TAG, "Telephony service is null, can't call "
"cancelMissedCallsNotification");
}
} catch (RemoteException e) {
Log.e(TAG, "Failed to clear missed calls notification due to remote exception");
} catch (IllegalArgumentException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IllegalAccessException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (InvocationTargetException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (SecurityException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (NoSuchMethodException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
/**
* Format the given phone number using
* {@link PhoneNumberUtils#formatNumber(android.text.Editable, int)}. This
* helper method uses {@link #sEditable} and {@link #sFormattingType} to
* prevent allocations between multiple calls.
* <p>
* Because of the shared {@link #sEditable} builder, <b>this method is not
* thread safe</b>, and should only be called from the GUI thread.
* <p>
* If the given String object is null or empty, return an empty String.
*/
private String formatPhoneNumber(String number) {
if (TextUtils.isEmpty(number)) {
return "";
}
// Cache formatting type if not already present
if (sFormattingType == FORMATTING_TYPE_INVALID) {
sFormattingType = PhoneNumberUtils.getFormatTypeForLocale(Locale.getDefault());
}
sEditable.clear();
sEditable.append(number);
PhoneNumberUtils.formatNumber(sEditable, sFormattingType);
return sEditable.toString();
}
private void resetNewCallsFlag() {
// Mark all "new" missed calls as not new anymore
StringBuilder where = new StringBuilder("type=");
where.append(Calls.MISSED_TYPE);
where.append(" AND new=1");
ContentValues values = new ContentValues(1);
values.put(Calls.NEW, "0");
mQueryHandler.startUpdate(UPDATE_TOKEN, null, Calls.CONTENT_URI,
values, where.toString(), null);
}
private void startQuery() {
mAdapter.setLoading(true);
// Cancel any pending queries
mQueryHandler.cancelOperation(QUERY_TOKEN);
mQueryHandler.startQuery(QUERY_TOKEN, null, Calls.CONTENT_URI,
CALL_LOG_PROJECTION, null, null, Calls.DEFAULT_SORT_ORDER);
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
SubMenu clearMenu = menu.addSubMenu(1, MENU_ITEM_CLEAR_CALL_LOG, 0, R.string.recent_calls_clear_call_log)
.setIcon(android.R.drawable.ic_menu_close_clear_cancel)
.setHeaderTitle(R.string.recent_calls_clear_what);
clearMenu.add(0, MENU_ITEM_CLEAR_ALL, 0, R.string.recent_calls_clear_all);
clearMenu.add(0, MENU_ITEM_CLEAR_INCOMING, 0, R.string.recent_calls_clear_incoming);
clearMenu.add(0, MENU_ITEM_CLEAR_OUTGOING, 0, R.string.recent_calls_clear_outgoing);
clearMenu.add(0, MENU_ITEM_CLEAR_MISSED, 0, R.string.recent_calls_clear_missed);
mPreferences = menu.add(0, MENU_PREFERENCES, 0, R.string.menu_preferences).setIcon(android.R.drawable.ic_menu_preferences);
//Wysie_Soh: Preferences intent
//mPreferences.setIntent(new Intent(this, ContactsPreferences.class));
return true;
}
@Override
public boolean onPrepareOptionsMenu(Menu menu) {
menu.setGroupVisible(1, (mRecordCount > 0));
return true;
}
@Override
public void onCreateContextMenu(ContextMenu menu, View view, ContextMenuInfo menuInfoIn) {
AdapterView.AdapterContextMenuInfo menuInfo;
try {
menuInfo = (AdapterView.AdapterContextMenuInfo) menuInfoIn;
} catch (ClassCastException e) {
Log.e(TAG, "bad menuInfoIn", e);
return;
}
Cursor cursor = (Cursor) mAdapter.getItem(menuInfo.position);
String number = cursor.getString(NUMBER_COLUMN_INDEX);
Uri numberUri = null;
boolean isVoicemail = false;
if (number.equals(CallerInfo.UNKNOWN_NUMBER)) {
number = getString(R.string.unknown);
} else if (number.equals(CallerInfo.PRIVATE_NUMBER)) {
number = getString(R.string.private_num);
} else if (number.equals(CallerInfo.PAYPHONE_NUMBER)) {
number = getString(R.string.payphone);
} else if (PhoneNumberUtils.extractNetworkPortion(number).equals(mVoiceMailNumber)) {
number = getString(R.string.voicemail);
numberUri = Uri.parse("voicemail:x");
isVoicemail = true;
} else {
numberUri = Uri.fromParts("tel", number, null);
}
ContactInfo info = mAdapter.getContactInfo(number);
boolean contactInfoPresent = (info != null && info != ContactInfo.EMPTY);
if (contactInfoPresent) {
menu.setHeaderTitle(info.name);
} else {
menu.setHeaderTitle(number);
}
if (numberUri != null) {
Intent intent = new Intent(Intent.ACTION_CALL, numberUri);
menu.add(0, 0, 0, getResources().getString(R.string.recentCalls_callNumber, number))
.setIntent(intent);
}
if (contactInfoPresent) {
menu.add(0, 0, 0, R.string.menu_viewContact)
.setIntent(new Intent(Intent.ACTION_VIEW,
ContentUris.withAppendedId(Contacts.CONTENT_URI, info.personId)));
}
if (numberUri != null && !isVoicemail) {
menu.add(0, 0, 0, R.string.recentCalls_editNumberBeforeCall)
.setIntent(new Intent(Intent.ACTION_DIAL, numberUri));
menu.add(0, 0, 0, R.string.menu_sendTextMessage)
.setIntent(new Intent(Intent.ACTION_SENDTO,
Uri.fromParts("sms", number, null)));
}
if (!contactInfoPresent && numberUri != null && !isVoicemail) {
Intent intent = new Intent(Intent.ACTION_INSERT_OR_EDIT);
intent.setType(Contacts.CONTENT_ITEM_TYPE);
intent.putExtra(Insert.PHONE, number);
menu.add(0, 0, 0, R.string.recentCalls_addToContact)
.setIntent(intent);
menu.add(0, MENU_ITEM_BLACKLIST, 0, R.string.recentCalls_addToBlacklist);
}
menu.add(0, MENU_ITEM_DELETE, 0, R.string.recentCalls_removeFromRecentList);
}
@Override
protected Dialog onCreateDialog(int id, Bundle args) {
switch (id) {
case DIALOG_CONFIRM_DELETE_ALL:
return new AlertDialog.Builder(this)
.setTitle(R.string.clearCallLogConfirmation_title)
.setIcon(android.R.drawable.ic_dialog_alert)
.setMessage(R.string.clearCallLogConfirmation)
.setNegativeButton(android.R.string.cancel, null)
.setPositiveButton(android.R.string.ok, new OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
getContentResolver().delete(Calls.CONTENT_URI, null, null);
// TODO The change notification should do this automatically, but it
// isn't working right now. Remove this when the change notification
// is working properly.
startQuery();
}
})
.setCancelable(false)
.create();
}
return null;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case MENU_ITEM_CLEAR_ALL: {
clearCallLog();
return true;
}
case MENU_ITEM_CLEAR_INCOMING: {
clearCallLogType(Calls.INCOMING_TYPE);
return true;
}
case MENU_ITEM_CLEAR_OUTGOING: {
clearCallLogType(Calls.OUTGOING_TYPE);
return true;
}
case MENU_ITEM_CLEAR_MISSED: {
clearCallLogType(Calls.MISSED_TYPE);
return true;
}
}
return super.onOptionsItemSelected(item);
}
@Override
public boolean onContextItemSelected(MenuItem item) {
// Convert the menu info to the proper type
AdapterView.AdapterContextMenuInfo menuInfo;
try {
menuInfo = (AdapterView.AdapterContextMenuInfo) item.getMenuInfo();
} catch (ClassCastException e) {
Log.e(TAG, "bad menuInfoIn", e);
return false;
}
Cursor cursor = (Cursor)mAdapter.getItem(menuInfo.position);
switch (item.getItemId()) {
case MENU_ITEM_DELETE: {
int groupSize = 1;
if (mAdapter.isGroupHeader(menuInfo.position)) {
groupSize = mAdapter.getGroupSize(menuInfo.position);
}
StringBuilder sb = new StringBuilder();
for (int i = 0; i < groupSize; i ) {
if (i != 0) {
sb.append(",");
cursor.moveToNext();
}
long id = cursor.getLong(ID_COLUMN_INDEX);
sb.append(id);
}
getContentResolver().delete(Calls.CONTENT_URI, Calls._ID " IN (" sb ")",
null);
}break;
case MENU_ITEM_BLACKLIST: {
Intent intent = new Intent(INSERT_BLACKLIST);
intent.putExtra("Insert.BLACKLIST", cursor.getString(NUMBER_COLUMN_INDEX));
sendBroadcast(intent);
}break;
}
return super.onContextItemSelected(item);
}
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
switch (keyCode) {
case KeyEvent.KEYCODE_CALL: {
long callPressDiff = SystemClock.uptimeMillis() - event.getDownTime();
if (callPressDiff >= ViewConfiguration.getLongPressTimeout()) {
// Launch voice dialer
Intent intent = new Intent(Intent.ACTION_VOICE_COMMAND);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
try {
startActivity(intent);
} catch (ActivityNotFoundException e) {
}
return true;
}
}
}
return super.onKeyDown(keyCode, event);
}
@Override
public boolean onKeyUp(int keyCode, KeyEvent event) {
switch (keyCode) {
case KeyEvent.KEYCODE_CALL:
try {
//ITelephony iTelephony = ITelephony.Stub.asInterface(ServiceManager.getService("phone"));
// 鍒濆鍖杋Telephony
Class<TelephonyManager> c = TelephonyManager.class;
TelephonyManager mTelephonyMgr = (TelephonyManager) this.getSystemService(Context.TELEPHONY_SERVICE);
TelephonyManager tManager = (TelephonyManager) this.getSystemService(Context.TELEPHONY_SERVICE);
Method getITelephonyMethod = getITelephonyMethod = c.getDeclaredMethod("getITelephony",(Class[]) null);
getITelephonyMethod.setAccessible(true);
ITelephony phone = (ITelephony)getITelephonyMethod.invoke(tManager, (Object[]) null);
if (phone != null && !phone.isIdle()) {
// Let the super class handle it
break;
}
} catch (RemoteException re) {
// Fall through and try to call the contact
} catch (IllegalArgumentException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IllegalAccessException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (InvocationTargetException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (SecurityException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (NoSuchMethodException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
callEntry(getListView().getSelectedItemPosition());
return true;
}
return super.onKeyUp(keyCode, event);
}
/*
* Get the number from the Contacts, if available, since sometimes
* the number provided by caller id may not be formatted properly
* depending on the carrier (roaming) in use at the time of the
* incoming call.
* Logic : If the caller-id number starts with a " ", use it
* Else if the number in the contacts starts with a " ", use that one
* Else if the number in the contacts is longer, use that one
*/
private String getBetterNumberFromContacts(String number) {
String matchingNumber = null;
// Look in the cache first. If it's not found then query the Phones db
ContactInfo ci = mAdapter.mContactInfo.get(number);
if (ci != null && ci != ContactInfo.EMPTY) {
matchingNumber = ci.number;
} else {
try {
Cursor phonesCursor =
RecentListActivity.this.getContentResolver().query(
Uri.withAppendedPath(PhoneLookup.CONTENT_FILTER_URI,
number),
PHONES_PROJECTION, null, null, null);
if (phonesCursor != null) {
if (phonesCursor.moveToFirst()) {
matchingNumber = phonesCursor.getString(MATCHED_NUMBER_COLUMN_INDEX);
}
phonesCursor.close();
}
} catch (Exception e) {
// Use the number from the call log
}
}
if (!TextUtils.isEmpty(matchingNumber) &&
(matchingNumber.startsWith(" ")
|| matchingNumber.length() > number.length())) {
number = matchingNumber;
}
return number;
}
private void callEntry(int position) {
if (position < 0) {
// In touch mode you may often not have something selected, so
// just call the first entry to make sure that [send] [send] calls the
// most recent entry.
position = 0;
}
final Cursor cursor = (Cursor)mAdapter.getItem(position);
if (cursor != null) {
String number = cursor.getString(NUMBER_COLUMN_INDEX);
if (TextUtils.isEmpty(number)
|| number.equals(CallerInfo.UNKNOWN_NUMBER)
|| number.equals(CallerInfo.PRIVATE_NUMBER)
|| number.equals(CallerInfo.PAYPHONE_NUMBER)) {
// This number can't be called, do nothing
return;
}
int callType = cursor.getInt(CALL_TYPE_COLUMN_INDEX);
if (!number.startsWith(" ") &&
(callType == Calls.INCOMING_TYPE
|| callType == Calls.MISSED_TYPE)) {
// If the caller-id matches a contact with a better qualified number, use it
number = getBetterNumberFromContacts(number);
}
Intent intent = new Intent(Intent.ACTION_CALL,
Uri.fromParts("tel", number, null));
intent.setFlags(
Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
startActivity(intent);
}
}
@Override
protected void onListItemClick(ListView l, View v, int position, long id) {
if (mAdapter.isGroupHeader(position)) {
mAdapter.toggleGroup(position);
} else {
/*Intent intent = new Intent(this, CallDetailActivity.class);
intent.setData(ContentUris.withAppendedId(CallLog.Calls.CONTENT_URI, id));
startActivity(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);
}
}
// Wysie: Dialog to confirm if user wants to clear call log
private void clearCallLog() {
if (ePrefs.getBoolean("cl_ask_before_clear", false)) {
AlertDialog.Builder alert = new AlertDialog.Builder(this);
alert.setTitle(R.string.alert_clear_call_log_title);
alert.setMessage(R.string.alert_clear_call_log_message);
alert.setPositiveButton(android.R.string.ok,
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int whichButton) {
deleteCallLog(null, null);
}
});
alert.setNegativeButton(android.R.string.cancel,
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int whichButton) {// Canceled.
}
});
alert.show();
} else {
deleteCallLog(null, null);
}
}
private void deleteCallLog(String where, String[] selArgs) {
try {
getContentResolver().delete(Calls.CONTENT_URI, where, selArgs);
// TODO The change notification should do this automatically, but it isn't working
// right now. Remove this when the change notification is working properly.
startQuery();
} catch (SQLiteException sqle) {// Nothing :P
}
}
private void clearCallLogType(final int type) {
int msg = 0;
if (type == Calls.INCOMING_TYPE) {
msg = R.string.alert_clear_cl_all_incoming;
} else if (type == Calls.OUTGOING_TYPE) {
msg = R.string.alert_clear_cl_all_outgoing;
} else if (type == Calls.MISSED_TYPE) {
msg = R.string.alert_clear_cl_all_missed;
}
if (ePrefs.getBoolean("cl_ask_before_clear", false)) {
AlertDialog.Builder alert = new AlertDialog.Builder(this);
alert.setTitle(R.string.alert_clear_call_log_title);
alert.setMessage(msg);
alert.setPositiveButton(android.R.string.ok,
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int whichButton) {
deleteCallLog(Calls.TYPE "=?", new String[] { Integer.toString(type) });
}
});
alert.setNegativeButton(android.R.string.cancel,
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int whichButton) {// Canceled.
}
});
alert.show();
} else {
deleteCallLog(Calls.TYPE "=?", new String[] { Integer.toString(type) });
}
}
}
好例子网口号:伸出你的我的手 — 分享!
小贴士
感谢您为本站写下的评论,您的评论对其它用户来说具有重要的参考价值,所以请认真填写。
- 类似“顶”、“沙发”之类没有营养的文字,对勤劳贡献的楼主来说是令人沮丧的反馈信息。
- 相信您也不想看到一排文字/表情墙,所以请不要反馈意义不大的重复字符,也请尽量不要纯表情的回复。
- 提问之前请再仔细看一遍楼主的说明,或许是您遗漏了。
- 请勿到处挖坑绊人、招贴广告。既占空间让人厌烦,又没人会搭理,于人于己都无利。
关于好例子网
本站旨在为广大IT学习爱好者提供一个非营利性互相学习交流分享平台。本站所有资源都可以被免费获取学习研究。本站资源来自网友分享,对搜索内容的合法性不具有预见性、识别性、控制性,仅供学习研究,请务必在下载后24小时内给予删除,不得用于其他任何用途,否则后果自负。基于互联网的特殊性,平台无法对用户传输的作品、信息、内容的权属或合法性、安全性、合规性、真实性、科学性、完整权、有效性等进行实质审查;无论平台是否已进行审查,用户均应自行承担因其传输的作品、信息、内容而可能或已经产生的侵权或权属纠纷等法律责任。本站所有资源不代表本站的观点或立场,基于网友分享,根据中国法律《信息网络传播权保护条例》第二十二与二十三条之规定,若资源存在侵权或相关问题请联系本站客服人员,点此联系我们。关于更多版权及免责申明参见 版权及免责申明


网友评论
我要评论