/* * Copyright (C) 2011 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.dialer.app.calllog; import android.Manifest.permission; import android.annotation.SuppressLint; import android.app.Activity; import android.content.Context; import android.content.Intent; import android.content.res.Resources; import android.net.Uri; import android.os.AsyncTask; import android.provider.CallLog; import android.provider.CallLog.Calls; import android.provider.ContactsContract.CommonDataKinds.Phone; import android.support.annotation.IntDef; import android.support.annotation.Nullable; import android.support.annotation.RequiresPermission; import android.support.annotation.VisibleForTesting; import android.support.v7.widget.CardView; import android.support.v7.widget.RecyclerView; import android.telecom.PhoneAccount; import android.telecom.PhoneAccountHandle; import android.telecom.TelecomManager; import android.telecom.VideoProfile; import android.telephony.PhoneNumberUtils; import android.telephony.TelephonyManager; import android.text.BidiFormatter; import android.text.TextDirectionHeuristics; import android.text.TextUtils; import android.view.ContextMenu; import android.view.LayoutInflater; import android.view.MenuItem; import android.view.View; import android.view.ViewStub; import android.widget.ImageButton; import android.widget.ImageView; import android.widget.TextView; import com.android.contacts.common.dialog.CallSubjectDialog; import com.android.dialer.app.R; import com.android.dialer.app.calllog.CallLogAdapter.OnActionModeStateChangedListener; import com.android.dialer.app.calllog.calllogcache.CallLogCache; import com.android.dialer.app.voicemail.VoicemailPlaybackLayout; import com.android.dialer.app.voicemail.VoicemailPlaybackPresenter; import com.android.dialer.blocking.BlockedNumbersMigrator; import com.android.dialer.blocking.FilteredNumberCompat; import com.android.dialer.blocking.FilteredNumbersUtil; import com.android.dialer.callcomposer.CallComposerActivity; import com.android.dialer.calldetails.CallDetailsEntries; import com.android.dialer.calldetails.OldCallDetailsActivity; import com.android.dialer.callintent.CallIntentBuilder; import com.android.dialer.calllogutils.CallbackActionHelper.CallbackAction; import com.android.dialer.clipboard.ClipboardUtils; import com.android.dialer.common.Assert; import com.android.dialer.common.LogUtil; import com.android.dialer.common.concurrent.AsyncTaskExecutors; import com.android.dialer.configprovider.ConfigProviderComponent; import com.android.dialer.constants.ActivityRequestCodes; import com.android.dialer.contactphoto.ContactPhotoManager; import com.android.dialer.dialercontact.DialerContact; import com.android.dialer.dialercontact.SimDetails; import com.android.dialer.duo.Duo; import com.android.dialer.duo.DuoComponent; import com.android.dialer.lettertile.LetterTileDrawable; import com.android.dialer.lettertile.LetterTileDrawable.ContactType; import com.android.dialer.logging.ContactSource; import com.android.dialer.logging.ContactSource.Type; import com.android.dialer.logging.DialerImpression; import com.android.dialer.logging.InteractionEvent; import com.android.dialer.logging.Logger; import com.android.dialer.logging.ScreenEvent; import com.android.dialer.logging.UiAction; import com.android.dialer.performancereport.PerformanceReport; import com.android.dialer.phonenumbercache.CachedNumberLookupService; import com.android.dialer.phonenumbercache.ContactInfo; import com.android.dialer.phonenumbercache.PhoneNumberCache; import com.android.dialer.phonenumberutil.PhoneNumberHelper; import com.android.dialer.telecom.TelecomUtil; import com.android.dialer.util.CallUtil; import com.android.dialer.util.DialerUtils; import com.android.dialer.util.UriUtils; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.ref.WeakReference; /** * This is an object containing references to views contained by the call log list item. This * improves performance by reducing the frequency with which we need to find views by IDs. * *
This object also contains UI logic pertaining to the view, to isolate it from the
* CallLogAdapter.
*/
public final class CallLogListItemViewHolder extends RecyclerView.ViewHolder
implements View.OnClickListener,
MenuItem.OnMenuItemClickListener,
View.OnCreateContextMenuListener {
private static final String TASK_DELETE = "task_delete";
/** The root view of the call log list item */
public final View rootView;
/** The quick contact badge for the contact. */
public final DialerQuickContactBadge quickContactView;
/** The primary action view of the entry. */
public final View primaryActionView;
/** The details of the phone call. */
public final PhoneCallDetailsViews phoneCallDetailsViews;
/** The text of the header for a day grouping. */
public final TextView dayGroupHeader;
/** The view containing the details for the call log row, including the action buttons. */
public final CardView callLogEntryView;
/** The actionable view which places a call to the number corresponding to the call log row. */
public final ImageView primaryActionButtonView;
private final Context context;
@Nullable private final PhoneAccountHandle defaultPhoneAccountHandle;
private final CallLogCache callLogCache;
private final CallLogListItemHelper callLogListItemHelper;
private final CachedNumberLookupService cachedNumberLookupService;
private final VoicemailPlaybackPresenter voicemailPlaybackPresenter;
private final OnClickListener blockReportListener;
@HostUi private final int hostUi;
/** Whether the data fields are populated by the worker thread, ready to be shown. */
public boolean isLoaded;
/** The view containing call log item actions. Null until the ViewStub is inflated. */
public View actionsView;
/** The button views below are assigned only when the action section is expanded. */
public VoicemailPlaybackLayout voicemailPlaybackView;
public View callButtonView;
public View videoCallButtonView;
public View setUpVideoButtonView;
public View inviteVideoButtonView;
public View createNewContactButtonView;
public View addToExistingContactButtonView;
public View sendMessageView;
public View blockReportView;
public View blockView;
public View unblockView;
public View reportNotSpamView;
public View detailsButtonView;
public View callWithNoteButtonView;
public View callComposeButtonView;
public View sendVoicemailButtonView;
public ImageView workIconView;
public ImageView checkBoxView;
/**
* The row Id for the first call associated with the call log entry. Used as a key for the map
* used to track which call log entries have the action button section expanded.
*/
public long rowId;
/**
* The call Ids for the calls represented by the current call log entry. Used when the user
* deletes a call log entry.
*/
public long[] callIds;
/**
* The callable phone number for the current call log entry. Cached here as the call back intent
* is set only when the actions ViewStub is inflated.
*/
@Nullable public String number;
/** The post-dial numbers that are dialed following the phone number. */
public String postDialDigits;
/** The formatted phone number to display. */
public String displayNumber;
/**
* The phone number presentation for the current call log entry. Cached here as the call back
* intent is set only when the actions ViewStub is inflated.
*/
public int numberPresentation;
/** The type of the phone number (e.g. main, work, etc). */
public String numberType;
/**
* The country iso for the call. Cached here as the call back intent is set only when the actions
* ViewStub is inflated.
*/
public String countryIso;
/**
* The type of call for the current call log entry. Cached here as the call back intent is set
* only when the actions ViewStub is inflated.
*/
public int callType;
/**
* ID for blocked numbers database. Set when context menu is created, if the number is blocked.
*/
public Integer blockId;
/**
* The account for the current call log entry. Cached here as the call back intent is set only
* when the actions ViewStub is inflated.
*/
public PhoneAccountHandle accountHandle;
/**
* If the call has an associated voicemail message, the URI of the voicemail message for playback.
* Cached here as the voicemail intent is only set when the actions ViewStub is inflated.
*/
public String voicemailUri;
/**
* The name or number associated with the call. Cached here for use when setting content
* descriptions on buttons in the actions ViewStub when it is inflated.
*/
@Nullable public CharSequence nameOrNumber;
/**
* The call type or Location associated with the call. Cached here for use when setting text for a
* voicemail log's call button
*/
public CharSequence callTypeOrLocation;
/** The contact info for the contact displayed in this list item. */
public volatile ContactInfo info;
/** Whether spam feature is enabled, which affects UI. */
public boolean isSpamFeatureEnabled;
/** Whether the current log entry is a spam number or not. */
public boolean isSpam;
public boolean isCallComposerCapable;
private View.OnClickListener expandCollapseListener;
private final OnActionModeStateChangedListener onActionModeStateChangedListener;
private final View.OnLongClickListener longPressListener;
private boolean voicemailPrimaryActionButtonClicked;
public int callbackAction;
public int dayGroupHeaderVisibility;
public CharSequence dayGroupHeaderText;
public boolean isAttachedToWindow;
public AsyncTask If the action views have never been shown yet for this view, inflate the view stub.
*/
public void showActions(boolean show) {
showOrHideVoicemailTranscriptionView(show);
if (show) {
if (!isLoaded) {
// a bug for some unidentified reason showActions() can be called before the item is
// loaded, causing NPE on uninitialized fields. Just log and return here, showActions() will
// be called again once the item is loaded.
LogUtil.e(
"CallLogListItemViewHolder.showActions",
"called before item is loaded",
new Exception());
return;
}
// Inflate the view stub if necessary, and wire up the event handlers.
inflateActionViewStub();
bindActionButtons();
actionsView.setVisibility(View.VISIBLE);
actionsView.setAlpha(1.0f);
} else {
// When recycling a view, it is possible the actionsView ViewStub was previously
// inflated so we should hide it in this case.
if (actionsView != null) {
actionsView.setVisibility(View.GONE);
}
}
updatePrimaryActionButton(show);
}
private void showOrHideVoicemailTranscriptionView(boolean isExpanded) {
if (callType != Calls.VOICEMAIL_TYPE) {
return;
}
View transcriptContainerView = phoneCallDetailsViews.transcriptionView;
TextView transcriptView = phoneCallDetailsViews.voicemailTranscriptionView;
TextView transcriptBrandingView = phoneCallDetailsViews.voicemailTranscriptionBrandingView;
if (!isExpanded) {
transcriptContainerView.setVisibility(View.GONE);
return;
}
boolean show = false;
if (TextUtils.isEmpty(transcriptView.getText())) {
transcriptView.setVisibility(View.GONE);
} else {
transcriptView.setVisibility(View.VISIBLE);
show = true;
}
if (TextUtils.isEmpty(transcriptBrandingView.getText())) {
transcriptBrandingView.setVisibility(View.GONE);
} else {
transcriptBrandingView.setVisibility(View.VISIBLE);
show = true;
}
if (show) {
transcriptContainerView.setVisibility(View.VISIBLE);
} else {
transcriptContainerView.setVisibility(View.GONE);
}
}
public void updatePhoto() {
quickContactView.assignContactUri(info.lookupUri);
if (isSpamFeatureEnabled && isSpam) {
quickContactView.setImageDrawable(context.getDrawable(R.drawable.blocked_contact));
return;
}
final String displayName = TextUtils.isEmpty(info.name) ? displayNumber : info.name;
ContactPhotoManager.getInstance(context)
.loadDialerThumbnailOrPhoto(
quickContactView,
info.lookupUri,
info.photoId,
info.photoUri,
displayName,
getContactType());
}
private @ContactType int getContactType() {
return LetterTileDrawable.getContactTypeFromPrimitives(
callLogCache.isVoicemailNumber(accountHandle, number),
isSpam,
cachedNumberLookupService != null && cachedNumberLookupService.isBusiness(info.sourceType),
numberPresentation,
false);
}
@Override
public void onClick(View view) {
if (view.getId() == R.id.primary_action_button) {
CallLogAsyncTaskUtil.markCallAsRead(context, callIds);
}
if (view.getId() == R.id.primary_action_button && !TextUtils.isEmpty(voicemailUri)) {
Logger.get(context).logImpression(DialerImpression.Type.VOICEMAIL_PLAY_AUDIO_DIRECTLY);
voicemailPrimaryActionButtonClicked = true;
expandCollapseListener.onClick(primaryActionView);
return;
}
if (view.getId() == R.id.call_with_note_action) {
CallSubjectDialog.start(
(Activity) context,
info.photoId,
info.photoUri,
info.lookupUri,
(String) nameOrNumber /* top line of contact view in call subject dialog */,
number,
TextUtils.isEmpty(info.name) ? null : displayNumber, /* second line of contact
view in dialog. */
numberType, /* phone number type (e.g. mobile) in second line of contact view */
getContactType(),
accountHandle);
return;
}
if (view.getId() == R.id.block_report_action) {
Logger.get(context).logImpression(DialerImpression.Type.CALL_LOG_BLOCK_REPORT_SPAM);
maybeShowBlockNumberMigrationDialog(
new BlockedNumbersMigrator.Listener() {
@Override
public void onComplete() {
blockReportListener.onBlockReportSpam(
displayNumber, number, countryIso, callType, info.sourceType);
}
});
return;
}
if (view.getId() == R.id.block_action) {
Logger.get(context).logImpression(DialerImpression.Type.CALL_LOG_BLOCK_NUMBER);
maybeShowBlockNumberMigrationDialog(
new BlockedNumbersMigrator.Listener() {
@Override
public void onComplete() {
blockReportListener.onBlock(
displayNumber, number, countryIso, callType, info.sourceType);
}
});
return;
}
if (view.getId() == R.id.unblock_action) {
Logger.get(context).logImpression(DialerImpression.Type.CALL_LOG_UNBLOCK_NUMBER);
blockReportListener.onUnblock(
displayNumber, number, countryIso, callType, info.sourceType, isSpam, blockId);
return;
}
if (view.getId() == R.id.report_not_spam_action) {
Logger.get(context).logImpression(DialerImpression.Type.CALL_LOG_REPORT_AS_NOT_SPAM);
blockReportListener.onReportNotSpam(
displayNumber, number, countryIso, callType, info.sourceType);
return;
}
if (view.getId() == R.id.call_compose_action) {
LogUtil.i("CallLogListItemViewHolder.onClick", "share and call pressed");
Logger.get(context).logImpression(DialerImpression.Type.CALL_LOG_SHARE_AND_CALL);
Activity activity = (Activity) context;
activity.startActivityForResult(
CallComposerActivity.newIntent(activity, buildContact()),
ActivityRequestCodes.DIALTACTS_CALL_COMPOSER);
return;
}
if (view.getId() == R.id.share_voicemail) {
Logger.get(context).logImpression(DialerImpression.Type.VVM_SHARE_PRESSED);
voicemailPlaybackPresenter.shareVoicemail();
return;
}
logCallLogAction(view.getId());
final IntentProvider intentProvider = (IntentProvider) view.getTag();
if (intentProvider == null) {
return;
}
intentProvider.logInteraction(context);
final Intent intent = intentProvider.getIntent(context);
// See IntentProvider.getCallDetailIntentProvider() for why this may be null.
if (intent == null) {
return;
}
if (OldCallDetailsActivity.isLaunchIntent(intent)) {
PerformanceReport.recordClick(UiAction.Type.OPEN_CALL_DETAIL);
((Activity) context)
.startActivityForResult(intent, ActivityRequestCodes.DIALTACTS_CALL_DETAILS);
} else {
if (Intent.ACTION_CALL.equals(intent.getAction())
&& intent.getIntExtra(TelecomManager.EXTRA_START_CALL_WITH_VIDEO_STATE, -1)
== VideoProfile.STATE_BIDIRECTIONAL) {
Logger.get(context).logImpression(DialerImpression.Type.IMS_VIDEO_REQUESTED_FROM_CALL_LOG);
}
DialerUtils.startActivityWithErrorToast(context, intent);
}
}
private static boolean isNonContactEntry(ContactInfo info) {
if (info == null || info.sourceType != Type.SOURCE_TYPE_DIRECTORY) {
return true;
}
return false;
}
private DialerContact buildContact() {
DialerContact.Builder contact = DialerContact.newBuilder();
contact.setPhotoId(info.photoId);
if (info.photoUri != null) {
contact.setPhotoUri(info.photoUri.toString());
}
if (info.lookupUri != null) {
contact.setContactUri(info.lookupUri.toString());
}
if (nameOrNumber != null) {
contact.setNameOrNumber((String) nameOrNumber);
}
contact.setContactType(getContactType());
if (number != null) {
contact.setNumber(number);
}
if (!TextUtils.isEmpty(postDialDigits)) {
contact.setPostDialDigits(postDialDigits);
}
/* second line of contact view. */
if (!TextUtils.isEmpty(info.name)) {
contact.setDisplayNumber(displayNumber);
}
/* phone number type (e.g. mobile) in second line of contact view */
contact.setNumberLabel(numberType);
/* third line of contact view. */
String accountLabel = callLogCache.getAccountLabel(accountHandle);
if (!TextUtils.isEmpty(accountLabel)) {
SimDetails.Builder simDetails = SimDetails.newBuilder().setNetwork(accountLabel);
simDetails.setColor(callLogCache.getAccountColor(accountHandle));
contact.setSimDetails(simDetails.build());
}
return contact.build();
}
private void logCallLogAction(int id) {
if (id == R.id.send_message_action) {
Logger.get(context).logImpression(DialerImpression.Type.CALL_LOG_SEND_MESSAGE);
} else if (id == R.id.add_to_existing_contact_action) {
Logger.get(context).logImpression(DialerImpression.Type.CALL_LOG_ADD_TO_CONTACT);
switch (hostUi) {
case HostUi.CALL_HISTORY:
Logger.get(context)
.logImpression(DialerImpression.Type.ADD_TO_A_CONTACT_FROM_CALL_HISTORY);
break;
case HostUi.CALL_LOG:
Logger.get(context).logImpression(DialerImpression.Type.ADD_TO_A_CONTACT_FROM_CALL_LOG);
break;
case HostUi.VOICEMAIL:
Logger.get(context).logImpression(DialerImpression.Type.ADD_TO_A_CONTACT_FROM_VOICEMAIL);
break;
default:
throw Assert.createIllegalStateFailException();
}
} else if (id == R.id.create_new_contact_action) {
Logger.get(context).logImpression(DialerImpression.Type.CALL_LOG_CREATE_NEW_CONTACT);
switch (hostUi) {
case HostUi.CALL_HISTORY:
Logger.get(context)
.logImpression(DialerImpression.Type.CREATE_NEW_CONTACT_FROM_CALL_HISTORY);
break;
case HostUi.CALL_LOG:
Logger.get(context).logImpression(DialerImpression.Type.CREATE_NEW_CONTACT_FROM_CALL_LOG);
break;
case HostUi.VOICEMAIL:
Logger.get(context)
.logImpression(DialerImpression.Type.CREATE_NEW_CONTACT_FROM_VOICEMAIL);
break;
default:
throw Assert.createIllegalStateFailException();
}
}
}
private void maybeShowBlockNumberMigrationDialog(BlockedNumbersMigrator.Listener listener) {
if (!FilteredNumberCompat.maybeShowBlockNumberMigrationDialog(
context, ((Activity) context).getFragmentManager(), listener)) {
listener.onComplete();
}
}
private void updateBlockReportActions(boolean canPlaceCallToNumber, boolean isVoicemailNumber) {
// Set block/spam actions.
blockReportView.setVisibility(View.GONE);
blockView.setVisibility(View.GONE);
unblockView.setVisibility(View.GONE);
reportNotSpamView.setVisibility(View.GONE);
String e164Number = PhoneNumberUtils.formatNumberToE164(number, countryIso);
if (!canPlaceCallToNumber
|| isVoicemailNumber
|| !FilteredNumbersUtil.canBlockNumber(context, e164Number, number)
|| !FilteredNumberCompat.canAttemptBlockOperations(context)) {
return;
}
boolean isBlocked = blockId != null;
if (isBlocked) {
unblockView.setVisibility(View.VISIBLE);
} else {
if (isSpamFeatureEnabled) {
if (isSpam) {
blockView.setVisibility(View.VISIBLE);
reportNotSpamView.setVisibility(View.VISIBLE);
} else {
blockReportView.setVisibility(View.VISIBLE);
}
} else {
blockView.setVisibility(View.VISIBLE);
}
}
}
public void setDetailedPhoneDetails(CallDetailsEntries callDetailsEntries) {
this.callDetailsEntries = callDetailsEntries;
}
@VisibleForTesting
public CallDetailsEntries getDetailedPhoneDetails() {
return callDetailsEntries;
}
@Override
public void onCreateContextMenu(
final ContextMenu menu, View v, ContextMenu.ContextMenuInfo menuInfo) {
if (TextUtils.isEmpty(number)) {
return;
}
if (callType == CallLog.Calls.VOICEMAIL_TYPE) {
menu.setHeaderTitle(context.getResources().getText(R.string.voicemail));
} else {
menu.setHeaderTitle(
PhoneNumberUtils.createTtsSpannable(
BidiFormatter.getInstance().unicodeWrap(number, TextDirectionHeuristics.LTR)));
}
menu.add(
ContextMenu.NONE,
R.id.context_menu_copy_to_clipboard,
ContextMenu.NONE,
R.string.action_copy_number_text)
.setOnMenuItemClickListener(this);
// The edit number before call does not show up if any of the conditions apply:
// 1) Number cannot be called
// 2) Number is the voicemail number
// 3) Number is a SIP address
if (PhoneNumberHelper.canPlaceCallsTo(number, numberPresentation)
&& !callLogCache.isVoicemailNumber(accountHandle, number)
&& !PhoneNumberHelper.isSipNumber(number)) {
menu.add(
ContextMenu.NONE,
R.id.context_menu_edit_before_call,
ContextMenu.NONE,
R.string.action_edit_number_before_call)
.setOnMenuItemClickListener(this);
}
if (callType == CallLog.Calls.VOICEMAIL_TYPE
&& phoneCallDetailsViews.voicemailTranscriptionView.length() > 0) {
menu.add(
ContextMenu.NONE,
R.id.context_menu_copy_transcript_to_clipboard,
ContextMenu.NONE,
R.string.copy_transcript_text)
.setOnMenuItemClickListener(this);
}
String e164Number = PhoneNumberUtils.formatNumberToE164(number, countryIso);
boolean isVoicemailNumber = callLogCache.isVoicemailNumber(accountHandle, number);
boolean canPlaceCallToNumber = PhoneNumberHelper.canPlaceCallsTo(number, numberPresentation);
if (canPlaceCallToNumber
&& !isVoicemailNumber
&& FilteredNumbersUtil.canBlockNumber(context, e164Number, number)
&& FilteredNumberCompat.canAttemptBlockOperations(context)) {
boolean isBlocked = blockId != null;
if (isBlocked) {
menu.add(
ContextMenu.NONE,
R.id.context_menu_unblock,
ContextMenu.NONE,
R.string.call_log_action_unblock_number)
.setOnMenuItemClickListener(this);
} else {
if (isSpamFeatureEnabled) {
if (isSpam) {
menu.add(
ContextMenu.NONE,
R.id.context_menu_report_not_spam,
ContextMenu.NONE,
R.string.call_log_action_remove_spam)
.setOnMenuItemClickListener(this);
menu.add(
ContextMenu.NONE,
R.id.context_menu_block,
ContextMenu.NONE,
R.string.call_log_action_block_number)
.setOnMenuItemClickListener(this);
} else {
menu.add(
ContextMenu.NONE,
R.id.context_menu_block_report_spam,
ContextMenu.NONE,
R.string.call_log_action_block_report_number)
.setOnMenuItemClickListener(this);
}
} else {
menu.add(
ContextMenu.NONE,
R.id.context_menu_block,
ContextMenu.NONE,
R.string.call_log_action_block_number)
.setOnMenuItemClickListener(this);
}
}
}
if (callType != CallLog.Calls.VOICEMAIL_TYPE) {
menu.add(ContextMenu.NONE, R.id.context_menu_delete, ContextMenu.NONE, R.string.delete)
.setOnMenuItemClickListener(this);
}
Logger.get(context).logScreenView(ScreenEvent.Type.CALL_LOG_CONTEXT_MENU, (Activity) context);
}
/** Specifies where the view holder belongs. */
@IntDef({HostUi.CALL_LOG, HostUi.CALL_HISTORY, HostUi.VOICEMAIL})
@Retention(RetentionPolicy.SOURCE)
private @interface HostUi {
int CALL_LOG = 0;
int CALL_HISTORY = 1;
int VOICEMAIL = 2;
}
public interface OnClickListener {
void onBlockReportSpam(
String displayNumber,
String number,
String countryIso,
int callType,
ContactSource.Type contactSourceType);
void onBlock(
String displayNumber,
String number,
String countryIso,
int callType,
ContactSource.Type contactSourceType);
void onUnblock(
String displayNumber,
String number,
String countryIso,
int callType,
ContactSource.Type contactSourceType,
boolean isSpam,
Integer blockId);
void onReportNotSpam(
String displayNumber,
String number,
String countryIso,
int callType,
ContactSource.Type contactSourceType);
}
private static class DeleteCallTask extends AsyncTask