实例介绍
【实例简介】
【实例截图】
【实例截图】
【核心代码】
/** * 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小时内给予删除,不得用于其他任何用途,否则后果自负。基于互联网的特殊性,平台无法对用户传输的作品、信息、内容的权属或合法性、安全性、合规性、真实性、科学性、完整权、有效性等进行实质审查;无论平台是否已进行审查,用户均应自行承担因其传输的作品、信息、内容而可能或已经产生的侵权或权属纠纷等法律责任。本站所有资源不代表本站的观点或立场,基于网友分享,根据中国法律《信息网络传播权保护条例》第二十二与二十三条之规定,若资源存在侵权或相关问题请联系本站客服人员,点此联系我们。关于更多版权及免责申明参见 版权及免责申明
网友评论
我要评论