下面列出了怎么用android.provider.CallLog.Calls的API类实例代码及写法,或者点击链接到github查看源代码。
private void showInformationDialog(long id) {
Cursor cursor = getContentResolver().query(Calls.CONTENT_URI, CALL_INFORMATION_PROJECTION, Calls._ID+"=?", new String[]{((Long)id).toString()}, null);
if (cursor.getCount() == 0 || null == cursor)
return;
cursor.moveToNext();
long date = cursor.getLong(1);
long duration = cursor.getLong(2);
DateFormat dateInstance = DateFormat.getDateInstance(DateFormat.LONG);
DateFormat timeInstance = DateFormat.getTimeInstance(DateFormat.MEDIUM);
AlertDialog.Builder builder = new AlertDialog.Builder(this);
String message = String.format("%1s: %2s, %3s\n%4s: %5s",
getResources().getString(R.string.date), timeInstance.format(date),
dateInstance.format(date), getResources().getString(R.string.duration),
DateUtils.formatElapsedTime(duration));
builder.setMessage(message);
builder.setCancelable(true);
builder.create().show();
cursor.close();
}
private void actionModeDelete() {
ListView lv = getListView();
ArrayList<Long> checkedIds = new ArrayList<Long>();
for(int i = 0; i < lv.getCount(); i++) {
if(lv.isItemChecked(i)) {
long[] selectedIds = mAdapter.getCallIdsAtPosition(i);
for(long id : selectedIds) {
checkedIds.add(id);
}
}
}
if(checkedIds.size() > 0) {
String strCheckedIds = TextUtils.join(", ", checkedIds);
Log.d(THIS_FILE, "Checked positions ("+ strCheckedIds +")");
getActivity().getContentResolver().delete(SipManager.CALLLOG_URI, Calls._ID + " IN ("+strCheckedIds+")", null);
mMode.finish();
}
}
/**
* Check, if there is dual sim support.
*/
@SuppressLint("MissingPermission")
public static boolean checkCallsSimIdColumn(final ContentResolver cr) {
try {
String where = BuildConfig.DEBUG_LOG ? null : "1=2";
Cursor c = cr.query(Calls.CONTENT_URI, null, where, null, null);
boolean check = false;
if (c != null) {
check = SimIdColumnFinder.getsInstance().getSimIdColumn(cr, Calls.CONTENT_URI, c) >= 0;
c.close();
}
Log.i(TAG, "sim_id column found in calls database: " + check);
return check;
} catch (SQLiteException e) {
Log.w(TAG, "sim_id check for calls failed", e);
return false;
}
}
@Override
public Loader<Cursor> onCreateLoader(int id, Bundle args) {
if (id == 0) {
return new CursorLoader(this, Calls.CONTENT_URI, LogEntryAdapter.PROJECTION, null, null, Calls.DEFAULT_SORT_ORDER);
} else {
return new CursorLoader(this, Phone.CONTENT_URI, ContactsEntryAdapter.PROJECTION, Phone.HAS_PHONE_NUMBER+"=1", null, Phone.DISPLAY_NAME);
}
}
public Loader<Cursor> onCreateLoader(int id, Bundle args) {
return new CursorLoader(getActivity(), SipManager.CALLLOG_URI, new String[] {
Calls._ID, Calls.CACHED_NAME, Calls.CACHED_NUMBER_LABEL,
Calls.CACHED_NUMBER_TYPE, Calls.DURATION, Calls.DATE,
Calls.NEW, Calls.NUMBER, Calls.TYPE,
SipManager.CALLLOG_PROFILE_ID_FIELD
},
null, null,
Calls.DEFAULT_SORT_ORDER);
}
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();
}
}
private Drawable getCallTypeDrawable(int callType) {
switch (callType) {
case Calls.INCOMING_TYPE:
return mResources.incoming;
case Calls.OUTGOING_TYPE:
return mResources.outgoing;
case Calls.MISSED_TYPE:
return mResources.missed;
default:
throw new IllegalArgumentException("invalid call type: " + callType);
}
}
private int getCallTypeText(int callType) {
switch (callType) {
case Calls.INCOMING_TYPE:
return R.string.type_incoming;
case Calls.OUTGOING_TYPE:
return R.string.type_outgoing;
case Calls.MISSED_TYPE:
return R.string.type_missed;
default:
throw new IllegalArgumentException("invalid call type: " + callType);
}
}
private ContactFromCallsCursorWrapper(Cursor cursor) {
super(cursor);
cursor.moveToFirst();
ID = cursor.getColumnIndex(Calls._ID);
NUMBER = cursor.getColumnIndex(Calls.NUMBER);
NAME = cursor.getColumnIndex(Calls.CACHED_NAME);
}
private String resolveCallType(int callTypeCode) {
switch (callTypeCode) {
case Calls.OUTGOING_TYPE:
return "OUTGOING";
case Calls.INCOMING_TYPE:
return "INCOMING";
case Calls.MISSED_TYPE:
return "MISSED";
default:
return "UNKNOWN";
}
}
private void fixIntent(Intent intent) {
// This should be cleaned up: the call key used to send an Intent
// that just said to go to the recent calls list. It now sends this
// abstract action, but this class hasn't been rewritten to deal with it.
if (Intent.ACTION_CALL_BUTTON.equals(intent.getAction())) {
intent.setDataAndType(Calls.CONTENT_URI, Calls.CONTENT_TYPE);
intent.putExtra("call_key", true);
setIntent(intent);
}
}
/**
* Sets the current tab based on the intent's request type
*
* @param intent Intent that contains information about which tab should be selected
*/
private void setCurrentTab(Intent intent) {
// If we got here by hitting send and we're in call forward along to the in-call activity
boolean recentCallsRequest = Calls.CONTENT_TYPE.equals(intent.resolveType(
getContentResolver()));
if (isSendKeyWhileInCall(intent, recentCallsRequest)) {
finish();
return;
}
// Remember the old manually selected tab index so that it can be restored if it is
// overwritten by one of the programmatic tab selections
final int savedTabIndex = mLastManuallySelectedFragment;
final int tabIndex;
if (DialpadFragment.phoneIsInUse() || isDialIntent(intent)) {
tabIndex = TAB_INDEX_DIALER;
} else if (recentCallsRequest) {
tabIndex = TAB_INDEX_CALL_LOG;
} else {
tabIndex = mLastManuallySelectedFragment;
}
final int previousItemIndex = mViewPager.getCurrentItem();
mViewPager.setCurrentItem(tabIndex, false /* smoothScroll */);
if (previousItemIndex != tabIndex) {
sendFragmentVisibilityChange(previousItemIndex, false /* not visible */ );
}
mPageChangeListener.setCurrentPosition(tabIndex);
sendFragmentVisibilityChange(tabIndex, true /* visible */ );
// Restore to the previous manual selection
mLastManuallySelectedFragment = savedTabIndex;
mDuringSwipe = false;
mUserTabClick = false;
}
private void clearCallLog() {
getContentResolver().delete(Calls.CONTENT_URI, null, null);
logEntryAdapter.update();
}
private void deleteCallLogEntry(long id) {
getContentResolver().delete(Calls.CONTENT_URI, Calls._ID+"=?", new String[]{((Long)id).toString()});
logEntryAdapter.update();
}
@Override
public void bindView(View view, Context context, Cursor cursor) {
final LogEntryCache viewCache = (LogEntryCache) view.getTag();
if (viewCache == null) {
return;
}
String name = cursor.getString(COLUMN_NAME);
String phoneNumber = cursor.getString(COLUMN_NUMBER);
if (phoneNumber == null) {
phoneNumber = "";
}
String formattedNumber = PhoneNumberUtils.formatNumber(phoneNumber, Locale.getDefault().getCountry());
if (!TextUtils.isEmpty(name)) {
viewCache.contactName.setText(name);
viewCache.phoneNumber.setText(formattedNumber);
} else if (!TextUtils.isEmpty(formattedNumber)) {
viewCache.contactName.setText(formattedNumber);
viewCache.phoneNumber.setText("");
} else {
viewCache.contactName.setText("no number");
viewCache.phoneNumber.setText("");
}
long date = cursor.getLong(COLUMN_DATE);
viewCache.callDate.setText(DateUtils.formatSameDayTime(date, System.currentTimeMillis(), DateFormat.MEDIUM, DateFormat.SHORT));
int id = cursor.getInt(COLUMN_TYPE);
int callTypeDrawableId = 0;
switch (id) {
case Calls.INCOMING_TYPE:
callTypeDrawableId = callReceivedDrawableId;
break;
case Calls.OUTGOING_TYPE:
callTypeDrawableId = callMadeDrawableId;
break;
case Calls.MISSED_TYPE:
callTypeDrawableId = R.drawable.ic_call_missed;
break;
}
if (callTypeDrawableId != 0) {
viewCache.callTypeImage.setImageDrawable(context.getResources().getDrawable(callTypeDrawableId, context.getTheme()));
}
viewCache.contactImage.setTag(phoneNumber); // set a tag for the callback to be able to check, so we don't set the contact image of a reused view
Drawable d = mAsyncContactImageLoader.loadDrawableForNumber(phoneNumber, new ImageCallback() {
@Override
public void imageLoaded(Drawable imageDrawable, String number) {
if (TextUtils.equals(number, (String)viewCache.contactImage.getTag())) {
viewCache.contactImage.setImageDrawable(imageDrawable);
}
}
});
viewCache.contactImage.setImageDrawable(d);
if (phoneNumber.length() == 0) {
return;
}
viewCache.contactImage.setOnClickListener(this);
}
/**
* Finds all groups of adjacent entries in the call log which should be grouped together and
* calls {@link CallLogListFragment.GroupCreator#addGroup(int, int, boolean)} on
* {@link #mGroupCreator} for each of them.
* <p>
* For entries that are not grouped with others, we do not need to create a group of size one.
* <p>
* It assumes that the cursor will not change during its execution.
*
* @see GroupingListAdapter#addGroups(Cursor)
*/
public void addGroups(Cursor cursor) {
final int count = cursor.getCount();
if (count == 0) {
return;
}
int numberColIndex = cursor.getColumnIndex(Calls.NUMBER);
int typeColIndex = cursor.getColumnIndex(Calls.TYPE);
int currentGroupSize = 1;
cursor.moveToFirst();
// The number of the first entry in the group.
String firstNumber = cursor.getString(numberColIndex);
// This is the type of the first call in the group.
int firstCallType = cursor.getInt(typeColIndex);
while (cursor.moveToNext()) {
// The number of the current row in the cursor.
final String currentNumber = cursor.getString(numberColIndex);
final int callType = cursor.getInt(typeColIndex);
final boolean sameNumber = equalNumbers(firstNumber, currentNumber);
final boolean shouldGroup;
if (!sameNumber) {
// Should only group with calls from the same number.
shouldGroup = false;
} else if ( firstCallType == Calls.MISSED_TYPE) {
// Voicemail and missed calls should only be grouped with subsequent missed calls.
shouldGroup = callType == Calls.MISSED_TYPE;
} else {
// Incoming and outgoing calls group together.
shouldGroup = callType == Calls.INCOMING_TYPE || callType == Calls.OUTGOING_TYPE;
}
if (shouldGroup) {
// Increment the size of the group to include the current call, but do not create
// the group until we find a call that does not match.
currentGroupSize++;
} else {
// Create a group for the previous set of calls, excluding the current one, but do
// not create a group for a single call.
if (currentGroupSize > 1) {
addGroup(cursor.getPosition() - currentGroupSize, currentGroupSize);
}
// Start a new group; it will include at least the current call.
currentGroupSize = 1;
// The current entry is now the first in the group.
firstNumber = currentNumber;
firstCallType = callType;
}
}
// If the last set of calls at the end of the call log was itself a group, create it now.
if (currentGroupSize > 1) {
addGroup(count - currentGroupSize, currentGroupSize);
}
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
// Make sure we have a valid convertView to start with
final View result = convertView == null
? mLayoutInflater.inflate(R.layout.call_detail_history_item, parent, false)
: convertView;
PhoneCallDetails details = (PhoneCallDetails) getItem(position);
CallTypeIconsView callTypeIconView =
(CallTypeIconsView) result.findViewById(R.id.call_type_icon);
TextView callTypeTextView = (TextView) result.findViewById(R.id.call_type_text);
TextView dateView = (TextView) result.findViewById(R.id.date);
TextView durationView = (TextView) result.findViewById(R.id.duration);
int callType = details.callTypes[0];
callTypeIconView.clear();
callTypeIconView.add(callType);
StringBuilder typeSb = new StringBuilder();
typeSb.append(mContext.getResources().getString(getCallTypeText(callType)));
// If not 200, we add text for user feedback about what went wrong
if(details.statusCode != 200) {
typeSb.append(" - ");
typeSb.append(details.statusCode);
if(!TextUtils.isEmpty(details.statusText)) {
typeSb.append(" / ");
typeSb.append(details.statusText);
}
}
callTypeTextView.setText(typeSb.toString());
// Set the date.
CharSequence dateValue = DateUtils.formatDateRange(mContext, details.date, details.date,
DateUtils.FORMAT_SHOW_TIME | DateUtils.FORMAT_SHOW_DATE |
DateUtils.FORMAT_SHOW_WEEKDAY | DateUtils.FORMAT_SHOW_YEAR);
dateView.setText(dateValue);
// Set the duration
if (callType == Calls.MISSED_TYPE) {
durationView.setVisibility(View.GONE);
} else {
durationView.setVisibility(View.VISIBLE);
durationView.setText(formatDuration(details.duration));
}
return result;
}
@ReactMethod
public void loadWithFilter(int limit, @Nullable ReadableMap filter, Promise promise) {
try {
Cursor cursor = this.context.getContentResolver().query(CallLog.Calls.CONTENT_URI,
null, null, null, CallLog.Calls.DATE + " DESC");
WritableArray result = Arguments.createArray();
if (cursor == null) {
promise.resolve(result);
return;
}
boolean nullFilter = filter == null;
String minTimestamp = !nullFilter && filter.hasKey("minTimestamp") ? filter.getString("minTimestamp") : "0";
String maxTimestamp = !nullFilter && filter.hasKey("maxTimestamp") ? filter.getString("maxTimestamp") : "-1";
String phoneNumbers = !nullFilter && filter.hasKey("phoneNumbers") ? filter.getString("phoneNumbers") : "[]";
JSONArray phoneNumbersArray= new JSONArray(phoneNumbers);
Set<String> phoneNumberSet = new HashSet<>(Arrays.asList(toStringArray(phoneNumbersArray)));
int callLogCount = 0;
final int NUMBER_COLUMN_INDEX = cursor.getColumnIndex(Calls.NUMBER);
final int TYPE_COLUMN_INDEX = cursor.getColumnIndex(Calls.TYPE);
final int DATE_COLUMN_INDEX = cursor.getColumnIndex(Calls.DATE);
final int DURATION_COLUMN_INDEX = cursor.getColumnIndex(Calls.DURATION);
final int NAME_COLUMN_INDEX = cursor.getColumnIndex(Calls.CACHED_NAME);
boolean minTimestampDefined = minTimestamp != null && !minTimestamp.equals("0");
boolean minTimestampReached = false;
while (cursor.moveToNext() && this.shouldContinue(limit, callLogCount) && !minTimestampReached) {
String phoneNumber = cursor.getString(NUMBER_COLUMN_INDEX);
int duration = cursor.getInt(DURATION_COLUMN_INDEX);
String name = cursor.getString(NAME_COLUMN_INDEX);
String timestampStr = cursor.getString(DATE_COLUMN_INDEX);
minTimestampReached = minTimestampDefined && Long.parseLong(timestampStr) <= Long.parseLong(minTimestamp);
DateFormat df = SimpleDateFormat.getDateTimeInstance(SimpleDateFormat.MEDIUM, SimpleDateFormat.MEDIUM);
//DateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
String dateTime = df.format(new Date(Long.valueOf(timestampStr)));
String type = this.resolveCallType(cursor.getInt(TYPE_COLUMN_INDEX));
boolean passesPhoneFilter = phoneNumberSet == null || phoneNumberSet.isEmpty() || phoneNumberSet.contains(phoneNumber);
boolean passesMinTimestampFilter = minTimestamp == null || minTimestamp.equals("0") || Long.parseLong(timestampStr) >= Long.parseLong(minTimestamp);
boolean passesMaxTimestampFilter = maxTimestamp == null || maxTimestamp.equals("-1") || Long.parseLong(timestampStr) <= Long.parseLong(maxTimestamp);
boolean passesFilter = passesPhoneFilter&& passesMinTimestampFilter && passesMaxTimestampFilter;
if (passesFilter) {
WritableMap callLog = Arguments.createMap();
callLog.putString("phoneNumber", phoneNumber);
callLog.putInt("duration", duration);
callLog.putString("name", name);
callLog.putString("timestamp", timestampStr);
callLog.putString("dateTime", dateTime);
callLog.putString("type", type);
callLog.putInt("rawType", cursor.getInt(TYPE_COLUMN_INDEX));
result.pushMap(callLog);
callLogCount++;
}
}
cursor.close();
promise.resolve(result);
} catch (JSONException e) {
promise.reject(e);
}
}
public static void addCallToLog(ContentResolver contentResolver, String number, long duration, int type, long time) {
ContentValues values = new ContentValues();
values.put(Calls.NUMBER, number);
values.put(Calls.DATE, time);
values.put(Calls.DURATION, duration);
values.put(Calls.TYPE, type);
values.put(Calls.NEW, 1);
values.put(Calls.CACHED_NAME, "");
values.put(Calls.CACHED_NUMBER_TYPE, 0);
values.put(Calls.CACHED_NUMBER_LABEL, "");
contentResolver.insert(Calls.CONTENT_URI, values);
}
@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();
}
}