Integrate spam feature into call logs.

+ Add block and report spam actions.
+ Show spam icon and label for call log item.
+ Add stableIds for CallLogAdapter to improve performance(reuse same
viewItem to minimize update work).
* Fix funky UI of call log.

Change-Id: I6144d70b6a8e38061e1447d0d01910d53026d1db
Fix: 27295728
Fix: 26907466
diff --git a/InCallUI/src/com/android/incallui/CallList.java b/InCallUI/src/com/android/incallui/CallList.java
index a3a9d74..61e8aa4 100644
--- a/InCallUI/src/com/android/incallui/CallList.java
+++ b/InCallUI/src/com/android/incallui/CallList.java
@@ -96,7 +96,7 @@
             onIncoming(call, call.getCannedSmsResponses());
             if (mExtendedCallInfoService != null) {
                 String number = TelecomCallUtil.getNumber(telecomCall);
-                mExtendedCallInfoService.getExtendedCallInfo(number,
+                mExtendedCallInfoService.getExtendedCallInfo(number, null,
                         new ExtendedCallInfoService.Listener() {
                             @Override
                             public void onComplete(boolean isSpam) {
diff --git a/res/layout/call_log_list_item_actions.xml b/res/layout/call_log_list_item_actions.xml
index 78203b7..4aad619 100644
--- a/res/layout/call_log_list_item_actions.xml
+++ b/res/layout/call_log_list_item_actions.xml
@@ -129,10 +129,61 @@
 
     </LinearLayout>
 
-    <ViewStub
-        android:id="@+id/extended_blocking_actions_container"
-        android:layout_width="match_parent"
-        android:layout_height="wrap_content"/>
+    <LinearLayout
+        android:id="@+id/block_report_action"
+        style="@style/CallLogActionStyle"
+        android:visibility="gone">
+
+        <ImageView
+            style="@style/CallLogActionIconStyle"
+            android:src="@drawable/ic_block_24dp"/>
+
+        <TextView
+            style="@style/CallLogActionTextStyle"
+            android:text="@string/call_log_action_block_report_number" />
+    </LinearLayout>
+
+    <LinearLayout
+        android:id="@+id/block_action"
+        style="@style/CallLogActionStyle"
+        android:visibility="gone">
+
+        <ImageView
+            style="@style/CallLogActionIconStyle"
+            android:src="@drawable/ic_block_24dp"/>
+
+        <TextView
+            style="@style/CallLogActionTextStyle"
+            android:text="@string/call_log_action_block_number" />
+    </LinearLayout>
+
+    <LinearLayout
+        android:id="@+id/unblock_action"
+        style="@style/CallLogActionStyle"
+        android:visibility="gone">
+
+        <ImageView
+            style="@style/CallLogActionIconStyle"
+            android:src="@drawable/ic_unblock"/>
+
+        <TextView
+            style="@style/CallLogActionTextStyle"
+            android:text="@string/call_log_action_unblock_number" />
+    </LinearLayout>
+
+    <LinearLayout
+        android:id="@+id/report_not_spam_action"
+        style="@style/CallLogActionStyle"
+        android:visibility="gone">
+
+        <ImageView
+            style="@style/CallLogActionIconStyle"
+            android:src="@drawable/ic_unblock"/>
+
+        <TextView
+            style="@style/CallLogActionTextStyle"
+            android:text="@string/call_log_action_remove_spam" />
+    </LinearLayout>
 
     <LinearLayout
         android:id="@+id/details_action"
diff --git a/res/values/strings.xml b/res/values/strings.xml
index ee9418a..5fca528 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -1043,4 +1043,27 @@
 
     <!-- Accessibility announcement to indicate which call is active -->
     <string name="accessibility_call_is_active"><xliff:g id="nameOrNumber">^1</xliff:g> is active</string>
+
+    <!-- Button text for a button displayed underneath an entry in the call log, which marks the
+             phone number represented by the call log entry as a Spam number.
+             [CHAR LIMIT=30] -->
+    <string name="call_log_action_block_report_number">Block/report spam</string>
+
+    <!-- Button text for a button displayed underneath an entry in the call log, which marks the
+         phone number represented by the call log entry as a Spam number.
+         [CHAR LIMIT=30] -->
+    <string name="call_log_action_block_number">Block</string>
+
+    <!-- Button text for a button displayed underneath an entry in the call log, which removes the
+         phone number represented by the call log entry from the Spam numbers list.
+         [CHAR LIMIT=30] -->
+    <string name="call_log_action_remove_spam">Not spam</string>
+
+    <!-- Button text for a button displayed underneath an entry in the call log, which removes the
+         phone number represented by the call log entry from the blacklisted numbers.
+         [CHAR LIMIT=30] -->
+    <string name="call_log_action_unblock_number">Unblock</string>
+
+    <!-- Label under the name of a spam number in the call log. [CHAR LIMIT=15] -->
+    <string name="spam_number_call_log_label">Spam</string>
 </resources>
diff --git a/src/com/android/dialer/PhoneCallDetails.java b/src/com/android/dialer/PhoneCallDetails.java
index b332b43..16192fc 100644
--- a/src/com/android/dialer/PhoneCallDetails.java
+++ b/src/com/android/dialer/PhoneCallDetails.java
@@ -111,6 +111,9 @@
      */
     public boolean isRead = true;
 
+    // If this call is a spam number.
+    public boolean isSpam = false;
+
     /**
      * Constructor with required fields for the details of a call with a number associated with a
      * contact.
diff --git a/src/com/android/dialer/calllog/CallLogAdapter.java b/src/com/android/dialer/calllog/CallLogAdapter.java
index 165594e..e826867 100644
--- a/src/com/android/dialer/calllog/CallLogAdapter.java
+++ b/src/com/android/dialer/calllog/CallLogAdapter.java
@@ -16,8 +16,13 @@
 
 package com.android.dialer.calllog;
 
+import com.android.dialer.compat.FilteredNumberCompat;
+import com.android.dialer.filterednumber.BlockNumberDialogFragment;
+import com.android.dialer.service.ExtendedCallInfoService;
+import com.android.dialerbind.ObjectFactory;
 import com.google.common.annotations.VisibleForTesting;
 
+import android.app.Activity;
 import android.content.Context;
 import android.content.Intent;
 import android.content.SharedPreferences;
@@ -32,10 +37,8 @@
 import android.support.v7.widget.RecyclerView;
 import android.support.v7.widget.RecyclerView.ViewHolder;
 import android.telecom.PhoneAccountHandle;
-import android.telephony.PhoneNumberUtils;
 import android.telephony.TelephonyManager;
 import android.text.TextUtils;
-import android.util.ArrayMap;
 import android.view.LayoutInflater;
 import android.view.View;
 import android.view.View.AccessibilityDelegate;
@@ -55,15 +58,12 @@
 import com.android.dialer.contactinfo.ContactInfoCache.OnContactInfoChangedListener;
 import com.android.dialer.database.FilteredNumberAsyncQueryHandler;
 import com.android.dialer.database.VoicemailArchiveContract;
-import com.android.dialer.filterednumber.BlockNumberDialogFragment.Callback;
 import com.android.dialer.logging.InteractionEvent;
 import com.android.dialer.logging.Logger;
-import com.android.dialer.service.ExtendedBlockingButtonRenderer;
 import com.android.dialer.util.PhoneNumberUtil;
 import com.android.dialer.voicemail.VoicemailPlaybackPresenter;
 
 import java.util.HashMap;
-import java.util.Map;
 
 /**
  * Adapter class to fill in data for the Call Log.
@@ -71,7 +71,7 @@
 public class CallLogAdapter extends GroupingListAdapter
         implements CallLogGroupBuilder.GroupCreator,
                 VoicemailPlaybackPresenter.OnVoicemailDeletedListener,
-                ExtendedBlockingButtonRenderer.Listener {
+                CallLogListItemViewHolder.OnClickListener {
 
     // Types of activities the call log adapter is used for
     public static final int ACTIVITY_TYPE_CALL_LOG = 1;
@@ -104,7 +104,6 @@
     protected final VoicemailPlaybackPresenter mVoicemailPlaybackPresenter;
     private final CallFetcher mCallFetcher;
     private final FilteredNumberAsyncQueryHandler mFilteredNumberAsyncQueryHandler;
-    private final Map<String, Boolean> mBlockedNumberCache = new ArrayMap<>();
 
     protected ContactInfoCache mContactInfoCache;
 
@@ -121,6 +120,26 @@
     private int mHiddenPosition = RecyclerView.NO_POSITION;
     private Uri mHiddenItemUri = null;
     private boolean mPendingHide = false;
+    private BlockNumberDialogFragment.Callback mBlockedNumberDialogCallback =
+            new BlockNumberDialogFragment.Callback() {
+                @Override
+                public void onFilterNumberSuccess() {
+                    Logger.logInteraction(
+                            InteractionEvent.BLOCK_NUMBER_CALL_LOG);
+                    notifyDataSetChanged();
+                }
+
+                @Override
+                public void onUnfilterNumberSuccess() {
+                    Logger.logInteraction(
+                            InteractionEvent.UNBLOCK_NUMBER_CALL_LOG);
+                    notifyDataSetChanged();
+                }
+
+                @Override
+                public void onChangeFilteredNumberUndo() {
+                }
+            };
 
     /**
      *  Hashmap, keyed by call Id, used to track the day group for a call.  As call log entries are
@@ -153,6 +172,8 @@
     /** Helper to group call log entries. */
     private final CallLogGroupBuilder mCallLogGroupBuilder;
 
+    private ExtendedCallInfoService mExtendedCallInfoService;
+
     /**
      * The OnClickListener used to expand or collapse the action buttons of a call log entry.
      */
@@ -296,6 +317,9 @@
         mPrefs = PreferenceManager.getDefaultSharedPreferences(context);
         mContactsPreferences = new ContactsPreferences(mContext);
         maybeShowVoicemailPromoCard();
+
+        mExtendedCallInfoService = ObjectFactory.newExtendedCallInfoService(context);
+        setHasStableIds(true);
     }
 
     public void onSaveInstanceState(Bundle outState) {
@@ -313,21 +337,33 @@
     }
 
     @Override
-    public void onBlockedNumber(String number,String countryIso) {
-        String cacheKey = PhoneNumberUtils.formatNumberToE164(number, countryIso);
-        if (!TextUtils.isEmpty(cacheKey)) {
-            mBlockedNumberCache.put(cacheKey, true);
-            notifyDataSetChanged();
-        }
+    public void onBlockReportSpam(String number, String countryIso, String displayNumber) {
+        mExtendedCallInfoService.reportSpam(number, countryIso);
+        notifyDataSetChanged();
     }
 
     @Override
-    public void onUnblockedNumber( String number, String countryIso) {
-        String cacheKey = PhoneNumberUtils.formatNumberToE164(number, countryIso);
-        if (!TextUtils.isEmpty(cacheKey)) {
-            mBlockedNumberCache.put(cacheKey, false);
-            notifyDataSetChanged();
-        }
+    public void onBlock(String number, String countryIso, String displayNumber) {
+        FilteredNumberCompat
+                .showBlockNumberDialogFlow(mContext.getContentResolver(), null, number,
+                        countryIso, displayNumber, R.id.floating_action_button_container,
+                        ((Activity) mContext).getFragmentManager(),
+                        mBlockedNumberDialogCallback);
+    }
+
+    @Override
+    public void onUnblock(String number, String countryIso, Integer blockId, String displayNumber) {
+        FilteredNumberCompat
+                .showBlockNumberDialogFlow(mContext.getContentResolver(), blockId, number,
+                        countryIso, displayNumber, R.id.floating_action_button_container,
+                        ((Activity) mContext).getFragmentManager(),
+                        mBlockedNumberDialogCallback);
+    }
+
+    @Override
+    public void onReportNotSpam(String number, String countryIso, String displayNumber) {
+        mExtendedCallInfoService.reportNotSpam(number, countryIso);
+        notifyDataSetChanged();
     }
 
     /**
@@ -412,22 +448,8 @@
                 mCallLogListItemHelper,
                 mVoicemailPlaybackPresenter,
                 mFilteredNumberAsyncQueryHandler,
-                new Callback() {
-                    @Override
-                    public void onFilterNumberSuccess() {
-                        Logger.logInteraction(
-                                InteractionEvent.BLOCK_NUMBER_CALL_LOG);
-                    }
-
-                    @Override
-                    public void onUnfilterNumberSuccess() {
-                        Logger.logInteraction(
-                                InteractionEvent.UNBLOCK_NUMBER_CALL_LOG);
-                    }
-
-                    @Override
-                    public void onChangeFilteredNumberUndo() {}
-                }, mActivityType == ACTIVITY_TYPE_ARCHIVE);
+                mBlockedNumberDialogCallback,
+                mActivityType == ACTIVITY_TYPE_ARCHIVE);
 
         viewHolder.callLogEntryView.setTag(viewHolder);
         viewHolder.callLogEntryView.setAccessibilityDelegate(mAccessibilityDelegate);
@@ -481,7 +503,40 @@
      * @param position The position of the list item.
      */
 
-    private void bindCallLogListViewHolder(ViewHolder viewHolder, int position) {
+    private void bindCallLogListViewHolder(final ViewHolder viewHolder, final int position) {
+        Cursor c = (Cursor) getItem(position);
+        if (c == null) {
+            return;
+        }
+
+        final String number = c.getString(CallLogQuery.NUMBER);
+        final String countryIso = c.getString(CallLogQuery.COUNTRY_ISO);
+
+        mFilteredNumberAsyncQueryHandler.isBlockedNumber(
+                new FilteredNumberAsyncQueryHandler.OnCheckBlockedListener() {
+                    @Override
+                    public void onCheckComplete(Integer id) {
+                        final CallLogListItemViewHolder views =
+                                (CallLogListItemViewHolder) viewHolder;
+                        views.blockId = id;
+                        if (mExtendedCallInfoService == null) {
+                            loadDataAndRender(views);
+                        } else {
+                            mExtendedCallInfoService.getExtendedCallInfo(number, countryIso,
+                                    new ExtendedCallInfoService.Listener() {
+                                        @Override
+                                        public void onComplete(boolean isSpam) {
+                                            views.isSpam = isSpam;
+                                            loadDataAndRender(views);
+                                        }
+                                    });
+                        }
+                    }
+                }, number, countryIso);
+    }
+
+    private void loadDataAndRender(CallLogListItemViewHolder views) {
+        int position = views.getAdapterPosition();
         Cursor c = (Cursor) getItem(position);
         if (c == null) {
             return;
@@ -544,7 +599,6 @@
             details.contactUserType = info.userType;
         }
 
-        final CallLogListItemViewHolder views = (CallLogListItemViewHolder) viewHolder;
         views.info = info;
         views.rowId = c.getLong(CallLogQuery.ID);
         // Store values used when the actions ViewStub is inflated on expansion.
@@ -589,19 +643,22 @@
             views.voicemailUri = c.getString(CallLogQuery.VOICEMAIL_URI);
         }
 
-        mCallLogListItemHelper.setPhoneCallDetails(views, details);
+        // Reversely pass spam information from views since details is not constructed when spam
+        // information comes back. This is used to render phone call details.
+        details.isSpam = views.isSpam;
+        render(views, details);
+    }
 
+    private void render(CallLogListItemViewHolder views, PhoneCallDetails details) {
+        mCallLogListItemHelper.setPhoneCallDetails(views, details);
         if (mCurrentlyExpandedRowId == views.rowId) {
             // In case ViewHolders were added/removed, update the expanded position if the rowIds
             // match so that we can restore the correct expanded state on rebind.
-            mCurrentlyExpandedPosition = position;
+            mCurrentlyExpandedPosition = views.getAdapterPosition();
             views.showActions(true);
         } else {
             views.showActions(false);
         }
-        views.updatePhoto();
-
-        mCallLogListItemHelper.setPhoneCallDetails(views, details);
     }
 
     private String getPreferredDisplayName(ContactInfo contactInfo) {
@@ -641,6 +698,16 @@
     }
 
     @Override
+    public long getItemId(int position) {
+        Cursor cursor = (Cursor) getItem(position);
+        if (cursor != null) {
+            return cursor.getLong(CallLogQuery.ID);
+        } else {
+            return 0;
+        }
+    }
+
+    @Override
     public int getGroupSize(int position) {
         return super.getGroupSize(position - (mShowVoicemailPromoCard ? 1 : 0));
     }
diff --git a/src/com/android/dialer/calllog/CallLogListItemHelper.java b/src/com/android/dialer/calllog/CallLogListItemHelper.java
index 5d2bc85..7919a09 100644
--- a/src/com/android/dialer/calllog/CallLogListItemHelper.java
+++ b/src/com/android/dialer/calllog/CallLogListItemHelper.java
@@ -82,6 +82,8 @@
 
         // Cache country iso. Used for number filtering.
         views.countryIso = details.countryIso;
+
+        views.updatePhoto();
     }
 
     /**
diff --git a/src/com/android/dialer/calllog/CallLogListItemViewHolder.java b/src/com/android/dialer/calllog/CallLogListItemViewHolder.java
index baf2e1a..53e6fbb 100644
--- a/src/com/android/dialer/calllog/CallLogListItemViewHolder.java
+++ b/src/com/android/dialer/calllog/CallLogListItemViewHolder.java
@@ -57,15 +57,10 @@
 import com.android.dialer.filterednumber.FilteredNumbersUtil;
 import com.android.dialer.logging.Logger;
 import com.android.dialer.logging.ScreenEvent;
-import com.android.dialer.service.ExtendedBlockingButtonRenderer;
 import com.android.dialer.util.DialerUtils;
 import com.android.dialer.util.PhoneNumberUtil;
 import com.android.dialer.voicemail.VoicemailPlaybackLayout;
 import com.android.dialer.voicemail.VoicemailPlaybackPresenter;
-import com.android.dialerbind.ObjectFactory;
-import com.google.common.collect.Lists;
-
-import java.util.List;
 
 /**
  * This is an object containing references to views contained by the call log list item. This
@@ -77,6 +72,13 @@
         implements View.OnClickListener, MenuItem.OnMenuItemClickListener,
         View.OnCreateContextMenuListener {
 
+    public interface OnClickListener {
+        void onBlockReportSpam(String number, String countryIso, String displayNumber);
+        void onBlock(String number, String countryIso, String displayNumber);
+        void onUnblock(String number, String countryIso, Integer blockId, String displayNumber);
+        void onReportNotSpam(String number, String countryIso, String displayNumber);
+    }
+
     /** The root view of the call log list item */
     public final View rootView;
     /** The quick contact badge for the contact. */
@@ -101,6 +103,10 @@
     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 ImageView workIconView;
@@ -198,9 +204,9 @@
     public ContactInfo info;
 
     /**
-     * Whether the current log entry is a blocked number or not. Used in updatePhoto()
+     * Whether the current log entry is a spam number or not. Used in updatePhoto()
      */
-    public boolean isBlocked;
+    public boolean isSpam;
 
     /**
      * Whether this is the archive tab or not.
@@ -212,19 +218,18 @@
     private final CallLogListItemHelper mCallLogListItemHelper;
     private final VoicemailPlaybackPresenter mVoicemailPlaybackPresenter;
     private final FilteredNumberAsyncQueryHandler mFilteredNumberAsyncQueryHandler;
+    private final OnClickListener mBlockReportListener;
 
     private final BlockNumberDialogFragment.Callback mFilteredNumberDialogCallback;
 
     private final int mPhotoSize;
-    private ViewStub mExtendedBlockingViewStub;
-    private final ExtendedBlockingButtonRenderer mExtendedBlockingButtonRenderer;
 
     private View.OnClickListener mExpandCollapseListener;
     private boolean mVoicemailPrimaryActionButtonClicked;
 
     private CallLogListItemViewHolder(
             Context context,
-            ExtendedBlockingButtonRenderer.Listener eventListener,
+            OnClickListener blockReportListener,
             View.OnClickListener expandCollapseListener,
             CallLogCache callLogCache,
             CallLogListItemHelper callLogListItemHelper,
@@ -248,6 +253,7 @@
         mVoicemailPlaybackPresenter = voicemailPlaybackPresenter;
         mFilteredNumberAsyncQueryHandler = filteredNumberAsyncQueryHandler;
         mFilteredNumberDialogCallback = filteredNumberDialogCallback;
+        mBlockReportListener = blockReportListener;
 
         this.rootView = rootView;
         this.quickContactView = quickContactView;
@@ -272,14 +278,12 @@
         primaryActionButtonView.setOnClickListener(this);
         primaryActionView.setOnClickListener(mExpandCollapseListener);
         primaryActionView.setOnCreateContextMenuListener(this);
-        mExtendedBlockingButtonRenderer =
-                ObjectFactory.newExtendedBlockingButtonRenderer(mContext, eventListener);
     }
 
     public static CallLogListItemViewHolder create(
             View view,
             Context context,
-            ExtendedBlockingButtonRenderer.Listener eventListener,
+            OnClickListener blockReportListener,
             View.OnClickListener expandCollapseListener,
             CallLogCache callLogCache,
             CallLogListItemHelper callLogListItemHelper,
@@ -290,7 +294,7 @@
 
         return new CallLogListItemViewHolder(
                 context,
-                eventListener,
+                blockReportListener,
                 expandCollapseListener,
                 callLogCache,
                 callLogListItemHelper,
@@ -428,14 +432,23 @@
             sendMessageView = actionsView.findViewById(R.id.send_message_action);
             sendMessageView.setOnClickListener(this);
 
+            blockReportView = actionsView.findViewById(R.id.block_report_action);
+            blockReportView.setOnClickListener(this);
+
+            blockView = actionsView.findViewById(R.id.block_action);
+            blockView.setOnClickListener(this);
+
+            unblockView = actionsView.findViewById(R.id.unblock_action);
+            unblockView.setOnClickListener(this);
+
+            reportNotSpamView = actionsView.findViewById(R.id.report_not_spam_action);
+            reportNotSpamView.setOnClickListener(this);
+
             detailsButtonView = actionsView.findViewById(R.id.details_action);
             detailsButtonView.setOnClickListener(this);
 
             callWithNoteButtonView = actionsView.findViewById(R.id.call_with_note_action);
             callWithNoteButtonView.setOnClickListener(this);
-
-            mExtendedBlockingViewStub =
-                    (ViewStub) actionsView.findViewById(R.id.extended_blocking_actions_container);
         }
 
         bindActionButtons();
@@ -568,32 +581,7 @@
         callWithNoteButtonView.setVisibility(
                 supportsCallSubject && !isVoicemailNumber ? View.VISIBLE : View.GONE);
 
-        if(mExtendedBlockingButtonRenderer != null){
-            List<View> completeLogListItems = Lists.newArrayList(
-                    createNewContactButtonView,
-                    addToExistingContactButtonView,
-                    sendMessageView,
-                    callButtonView,
-                    callWithNoteButtonView,
-                    detailsButtonView,
-                    voicemailPlaybackView);
-
-            List<View> blockedNumberVisibleViews = Lists.newArrayList(detailsButtonView);
-            List<View> extendedBlockingVisibleViews = Lists.newArrayList(detailsButtonView);
-
-            ExtendedBlockingButtonRenderer.ViewHolderInfo viewHolderInfo =
-                    new ExtendedBlockingButtonRenderer.ViewHolderInfo(
-                            completeLogListItems,
-                            extendedBlockingVisibleViews,
-                            blockedNumberVisibleViews,
-                            number,
-                            countryIso,
-                            nameOrNumber.toString(),
-                            displayNumber);
-            mExtendedBlockingButtonRenderer.setViewHolderInfo(viewHolderInfo);
-
-            mExtendedBlockingButtonRenderer.render(mExtendedBlockingViewStub);
-        }
+        updateBlockReportActions();
     }
 
     /**
@@ -635,6 +623,11 @@
     }
 
     public void updatePhoto() {
+        if (isSpam) {
+            quickContactView.setImageDrawable(
+                    mContext.getDrawable(R.drawable.blocked_contact));
+            return;
+        }
         quickContactView.assignContactUri(info.lookupUri);
 
         final boolean isVoicemail = mCallLogCache.isVoicemailNumber(accountHandle, number);
@@ -658,14 +651,6 @@
             ContactPhotoManager.getInstance(mContext).loadThumbnail(quickContactView, info.photoId,
                     false /* darkTheme */, true /* isCircular */, request);
         }
-
-        if (mExtendedBlockingButtonRenderer != null) {
-            mExtendedBlockingButtonRenderer.updatePhotoAndLabelIfNecessary(
-                    number,
-                    countryIso,
-                    quickContactView,
-                    phoneCallDetailsViews.callLocationAndDate);
-        }
     }
 
     @Override
@@ -686,6 +671,14 @@
                                                                            view in dialog. */
                     numberType, /* phone number type (e.g. mobile) in second line of contact view */
                     accountHandle);
+        } else if (view.getId() == R.id.block_report_action) {
+            mBlockReportListener.onBlockReportSpam(number, countryIso, displayNumber);
+        } else if (view.getId() == R.id.block_action) {
+            mBlockReportListener.onBlock(number, countryIso, displayNumber);
+        } else if (view.getId() == R.id.unblock_action) {
+            mBlockReportListener.onUnblock(number, countryIso, blockId, displayNumber);
+        } else if (view.getId() == R.id.report_not_spam_action) {
+            mBlockReportListener.onReportNotSpam(number, countryIso, displayNumber);
         } else {
             final IntentProvider intentProvider = (IntentProvider) view.getTag();
             if (intentProvider != null) {
@@ -729,4 +722,24 @@
         viewHolder.workIconView = new ImageButton(context);
         return viewHolder;
     }
+
+    private void updateBlockReportActions() {
+        // Set block/spam actions.
+        blockReportView.setVisibility(View.GONE);
+        blockView.setVisibility(View.GONE);
+        unblockView.setVisibility(View.GONE);
+        reportNotSpamView.setVisibility(View.GONE);
+        boolean isBlocked = blockId != null;
+        if (isBlocked) {
+            unblockView.setVisibility(View.VISIBLE);
+        } else {
+            if (isSpam) {
+                blockView.setVisibility(View.VISIBLE);
+                reportNotSpamView.setVisibility(View.VISIBLE);
+            } else {
+                blockReportView.setVisibility(View.VISIBLE);
+            }
+        }
+
+    }
 }
\ No newline at end of file
diff --git a/src/com/android/dialer/calllog/PhoneCallDetailsHelper.java b/src/com/android/dialer/calllog/PhoneCallDetailsHelper.java
index 7b149e2..ff1a44f 100644
--- a/src/com/android/dialer/calllog/PhoneCallDetailsHelper.java
+++ b/src/com/android/dialer/calllog/PhoneCallDetailsHelper.java
@@ -316,6 +316,11 @@
     /** Sets the call count, date, and if it is a voicemail, sets the duration. */
     private void setDetailText(PhoneCallDetailsViews views, Integer callCount,
                                PhoneCallDetails details) {
+        if (details.isSpam) {
+            views.callLocationAndDate.setText(
+                    mContext.getString(R.string.spam_number_call_log_label));
+            return;
+        }
         // Combine the count (if present) and the date.
         CharSequence dateText = getCallLocationAndDate(details);
         final CharSequence text;
@@ -333,7 +338,6 @@
         } else {
             views.callLocationAndDate.setText(text);
         }
-
     }
 
     private String getVoicemailDuration(PhoneCallDetails details) {
diff --git a/src/com/android/dialer/service/ExtendedCallInfoService.java b/src/com/android/dialer/service/ExtendedCallInfoService.java
index 33a85f4..412cece 100644
--- a/src/com/android/dialer/service/ExtendedCallInfoService.java
+++ b/src/com/android/dialer/service/ExtendedCallInfoService.java
@@ -35,7 +35,22 @@
     /**
      * Gets extended call information.
      * @param number The phone number of the call.
+     * @param countryIso The country ISO of the call.
      * @param listener The callback to be invoked after {@code Info} is fetched.
      */
-    void getExtendedCallInfo(String number, Listener listener);
+    void getExtendedCallInfo(String number, String countryIso, Listener listener);
+
+    /**
+     * Reports number as spam.
+     * @param number The number to be reported.
+     * @param countryIso The country ISO of the number.
+     */
+    void reportSpam(String number, String countryIso);
+
+    /**
+     * Reports number as not spam.
+     * @param number The number to be reported.
+     * @param countryIso The country ISO of the number.
+     */
+    void reportNotSpam(String number, String countryIso);
 }