Add spam indicator for notification and incoming call of incallui.

+ Add spam icon and label in notification of incoming call.
+ Add spam icon and label in primary call info of incall screen.
+ Change background color and status bar color if primary caller is spam.

Screenshots:
https://screenshot.googleplex.com/WFob2oPdXZd
https://screenshot.googleplex.com/ddCfg91Anef

Bug: 27611253
Change-Id: I03b3754f69631f5f6b07932b1a0006407b93c6e8
diff --git a/InCallUI/res/layout/primary_call_info.xml b/InCallUI/res/layout/primary_call_info.xml
index 3eb3630..56291bc 100644
--- a/InCallUI/res/layout/primary_call_info.xml
+++ b/InCallUI/res/layout/primary_call_info.xml
@@ -163,6 +163,14 @@
             android:scaleType="fitCenter"
             android:visibility="gone" />
 
+        <ImageView android:id="@+id/spamIcon"
+            android:src="@drawable/blocked_contact"
+            android:layout_width="24dp"
+            android:layout_height="match_parent"
+            android:layout_marginEnd="8dp"
+            android:scaleType="fitCenter"
+            android:visibility="gone" />
+
         <!-- Label (like "Mobile" or "Work", if present) and phone number, side by side -->
         <LinearLayout android:id="@+id/labelAndNumber"
             android:layout_width="wrap_content"
diff --git a/InCallUI/res/values/colors.xml b/InCallUI/res/values/colors.xml
index 99cf7b0..71c7f01 100644
--- a/InCallUI/res/values/colors.xml
+++ b/InCallUI/res/values/colors.xml
@@ -98,6 +98,7 @@
         <item>#7B1FA2</item>
         <item>#C2185B</item>
         <item>#C53929</item>
+        <item>#A52714</item>
     </array>
 
     <!-- Darker versions of background_colors, two shades darker. These colors are used for the
@@ -109,8 +110,12 @@
         <item>#6A1B9A</item>
         <item>#AD1457</item>
         <item>#B93221</item>
+        <item>#841F10</item>
     </array>
 
+    <!-- Background color for spam. This color must match one of background_colors above. -->
+    <color name="incall_call_spam_background_color">#A52714</color>
+
     <!-- Ripple color used over light backgrounds. -->
     <color name="ripple_light">#40000000</color>
 
diff --git a/InCallUI/res/values/strings.xml b/InCallUI/res/values/strings.xml
index 57a1b53..9534c0e 100644
--- a/InCallUI/res/values/strings.xml
+++ b/InCallUI/res/values/strings.xml
@@ -169,6 +169,8 @@
     <string name="notification_incoming_work_call_wifi">Incoming Wi-Fi work call</string>
     <!-- The "label" of the in-call Notification for an incoming ringing video call. -->
     <string name="notification_incoming_video_call">Incoming video call</string>
+    <!-- The "label" of the in-call Notification for an incoming ringing spam call. -->
+    <string name="notification_incoming_spam_call">Incoming suspected spam call</string>
     <!-- The "label" of the in-call Notification for upgrading an existing call to a video call. -->
     <string name="notification_requesting_video_call">Incoming video request</string>
     <!-- Label for the "Voicemail" notification item, when expanded. -->
@@ -523,4 +525,6 @@
     <string name="open_now">Open now</string>
     <!-- Displayed when a place is closed. -->
     <string name="closed_now">Closed now</string>
+    <!-- Label for spam call in primary info. [CHAR LIMIT=20] -->
+    <string name="label_spam_caller">Suspected spam caller</string>
 </resources>
diff --git a/InCallUI/src/com/android/incallui/Call.java b/InCallUI/src/com/android/incallui/Call.java
index 54ec528..483d53e 100644
--- a/InCallUI/src/com/android/incallui/Call.java
+++ b/InCallUI/src/com/android/incallui/Call.java
@@ -389,6 +389,16 @@
 
     private LogState mLogState = new LogState();
 
+    private boolean mIsSpam;
+
+    public void setSpam(boolean isSpam) {
+        mIsSpam = isSpam;
+    }
+
+    public boolean isSpam() {
+        return mIsSpam;
+    }
+
     /**
      * Used only to create mock calls for testing
      */
diff --git a/InCallUI/src/com/android/incallui/CallCardFragment.java b/InCallUI/src/com/android/incallui/CallCardFragment.java
index 785d3d3..54b37e5 100644
--- a/InCallUI/src/com/android/incallui/CallCardFragment.java
+++ b/InCallUI/src/com/android/incallui/CallCardFragment.java
@@ -130,6 +130,7 @@
     private TextView mCallTypeLabel;
     private ImageView mHdAudioIcon;
     private ImageView mForwardIcon;
+    private ImageView mSpamIcon;
     private View mCallNumberAndLabel;
     private TextView mElapsedTime;
     private Drawable mPrimaryPhotoDrawable;
@@ -280,6 +281,7 @@
         mCallStateLabel = (TextView) view.findViewById(R.id.callStateLabel);
         mHdAudioIcon = (ImageView) view.findViewById(R.id.hdAudioIcon);
         mForwardIcon = (ImageView) view.findViewById(R.id.forwardIcon);
+        mSpamIcon = (ImageView) view.findViewById(R.id.spamIcon);
         mCallNumberAndLabel = view.findViewById(R.id.labelAndNumber);
         mCallTypeLabel = (TextView) view.findViewById(R.id.callTypeLabel);
         mElapsedTime = (TextView) view.findViewById(R.id.elapsedTime);
@@ -1207,6 +1209,19 @@
         mForwardIcon.setVisibility(visible ? View.VISIBLE : View.GONE);
     }
 
+    /**
+     * Changes the visibility of the spam icon.
+     *
+     * @param visible {@code true} if the UI should show the spam icon.
+     */
+    @Override
+    public void showSpamIndicator(boolean visible) {
+        if (visible) {
+            mSpamIcon.setVisibility(View.VISIBLE);
+            mNumberLabel.setText(R.string.label_spam_caller);
+            mPhoneNumber.setVisibility(View.GONE);
+        }
+    }
 
     /**
      * Changes the visibility of the "manage conference call" button.
diff --git a/InCallUI/src/com/android/incallui/CallCardPresenter.java b/InCallUI/src/com/android/incallui/CallCardPresenter.java
index 36af7e2..5cc6bc0 100644
--- a/InCallUI/src/com/android/incallui/CallCardPresenter.java
+++ b/InCallUI/src/com/android/incallui/CallCardPresenter.java
@@ -458,6 +458,10 @@
         getUi().showHdAudioIndicator(showHdAudioIndicator);
     }
 
+    private void maybeShowSpamIconAndLabel() {
+        getUi().showSpamIndicator(mPrimary.isSpam());
+    }
+
     /**
      * Only show the conference call button if we can manage the conference.
      */
@@ -826,6 +830,7 @@
             boolean isEmergencyCall = mPrimary.isEmergencyCall();
             mEmergencyCallListener.onCallUpdated((BaseFragment) ui, isEmergencyCall);
         }
+        maybeShowSpamIconAndLabel();
     }
 
     private void updateSecondaryDisplayInfo() {
@@ -1162,6 +1167,7 @@
         void setProgressSpinnerVisible(boolean visible);
         void showHdAudioIndicator(boolean visible);
         void showForwardIndicator(boolean visible);
+        void showSpamIndicator(boolean visible);
         void showManageConferenceCallButton(boolean visible);
         boolean isManageConferenceVisible();
         boolean isCallSubjectVisible();
diff --git a/InCallUI/src/com/android/incallui/CallList.java b/InCallUI/src/com/android/incallui/CallList.java
index d0f3c10..a3a9d74 100644
--- a/InCallUI/src/com/android/incallui/CallList.java
+++ b/InCallUI/src/com/android/incallui/CallList.java
@@ -23,8 +23,8 @@
 import android.telecom.PhoneAccount;
 
 import com.android.contacts.common.testing.NeededForTesting;
-import com.android.dialer.database.FilteredNumberAsyncQueryHandler;
 import com.android.dialer.logging.Logger;
+import com.android.dialer.service.ExtendedCallInfoService;
 import com.android.incallui.util.TelecomCallUtil;
 
 import com.google.common.base.Preconditions;
@@ -69,7 +69,7 @@
             .newHashMap();
     private final Set<Call> mPendingDisconnectCalls = Collections.newSetFromMap(
             new ConcurrentHashMap<Call, Boolean>(8, 0.9f, 1));
-    private FilteredNumberAsyncQueryHandler mFilteredQueryHandler;
+    private ExtendedCallInfoService mExtendedCallInfoService;
 
     /**
      * Static singleton accessor method.
@@ -94,6 +94,17 @@
         if (call.getState() == Call.State.INCOMING ||
                 call.getState() == Call.State.CALL_WAITING) {
             onIncoming(call, call.getCannedSmsResponses());
+            if (mExtendedCallInfoService != null) {
+                String number = TelecomCallUtil.getNumber(telecomCall);
+                mExtendedCallInfoService.getExtendedCallInfo(number,
+                        new ExtendedCallInfoService.Listener() {
+                            @Override
+                            public void onComplete(boolean isSpam) {
+                                call.setSpam(isSpam);
+                                onUpdate(call);
+                            }
+                        });
+            }
         } else {
             onUpdate(call);
         }
@@ -615,8 +626,8 @@
         }
     };
 
-    public void setFilteredNumberQueryHandler(FilteredNumberAsyncQueryHandler handler) {
-        mFilteredQueryHandler = handler;
+    public void setExtendedCallInfoService(ExtendedCallInfoService service) {
+        mExtendedCallInfoService = service;
     }
 
     /**
diff --git a/InCallUI/src/com/android/incallui/InCallPresenter.java b/InCallUI/src/com/android/incallui/InCallPresenter.java
index 5a27b4c..2a3fd3e 100644
--- a/InCallUI/src/com/android/incallui/InCallPresenter.java
+++ b/InCallUI/src/com/android/incallui/InCallPresenter.java
@@ -341,7 +341,8 @@
         mFilteredQueryHandler = new FilteredNumberAsyncQueryHandler(context.getContentResolver());
         mTelephonyManager = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
         mTelephonyManager.listen(mPhoneStateListener, PhoneStateListener.LISTEN_CALL_STATE);
-        mCallList.setFilteredNumberQueryHandler(mFilteredQueryHandler);
+        mCallList.setExtendedCallInfoService(
+                com.android.dialerbind.ObjectFactory.newExtendedCallInfoService(context));
 
         Log.d(this, "Finished InCallPresenter.setUp");
     }
@@ -1744,7 +1745,14 @@
         if (call == null) {
             return getColorsFromPhoneAccountHandle(mPendingPhoneAccountHandle);
         } else {
-            return getColorsFromPhoneAccountHandle(call.getAccountHandle());
+            if (call.isSpam()) {
+                Resources resources = mContext.getResources();
+                return new InCallUIMaterialColorMapUtils(
+                        resources).calculatePrimaryAndSecondaryColor(
+                        resources.getColor(R.color.incall_call_spam_background_color));
+            } else {
+                return getColorsFromPhoneAccountHandle(call.getAccountHandle());
+            }
         }
     }
 
diff --git a/InCallUI/src/com/android/incallui/StatusBarNotifier.java b/InCallUI/src/com/android/incallui/StatusBarNotifier.java
index 7d212aa..21bf389 100644
--- a/InCallUI/src/com/android/incallui/StatusBarNotifier.java
+++ b/InCallUI/src/com/android/incallui/StatusBarNotifier.java
@@ -33,7 +33,9 @@
 import android.content.Intent;
 import android.graphics.Bitmap;
 import android.graphics.BitmapFactory;
+import android.graphics.Canvas;
 import android.graphics.drawable.BitmapDrawable;
+import android.graphics.drawable.Drawable;
 import android.media.AudioAttributes;
 import android.net.Uri;
 import android.provider.ContactsContract.Contacts;
@@ -52,6 +54,7 @@
 import com.android.contacts.common.util.BitmapUtil;
 import com.android.contacts.common.util.ContactDisplayUtils;
 import com.android.dialer.R;
+import com.android.dialer.service.ExtendedCallInfoService;
 import com.android.incallui.ContactInfoCache.ContactCacheEntry;
 import com.android.incallui.ContactInfoCache.ContactInfoCacheCallback;
 import com.android.incallui.InCallPresenter.InCallState;
@@ -463,6 +466,10 @@
         if (contactInfo.photo != null && (contactInfo.photo instanceof BitmapDrawable)) {
             largeIcon = ((BitmapDrawable) contactInfo.photo).getBitmap();
         }
+        if (call.isSpam()) {
+            Drawable drawable = mContext.getResources().getDrawable(R.drawable.blocked_contact);
+            largeIcon = CallCardFragment.drawableToBitmap(drawable);
+        }
         return largeIcon;
     }
 
@@ -524,7 +531,11 @@
             if (call.hasProperty(Details.PROPERTY_WIFI)) {
                 resId = R.string.notification_incoming_call_wifi;
             } else {
-                resId = R.string.notification_incoming_call;
+                if (call.isSpam()) {
+                    resId = R.string.notification_incoming_spam_call;
+                } else {
+                    resId = R.string.notification_incoming_call;
+                }
             }
         } else if (call.getState() == Call.State.ONHOLD) {
             resId = R.string.notification_on_hold;
diff --git a/res/values/colors.xml b/res/values/colors.xml
index 38fd6b3..f2cbea1 100644
--- a/res/values/colors.xml
+++ b/res/values/colors.xml
@@ -124,7 +124,7 @@
     <color name="divider_line_color">#D8D8D8</color>
 
     <!--  Colors for blocked numbers list -->
-    <color name="blocked_contact_background">@android:color/holo_red_dark</color>
+    <color name="blocked_contact_background">#A52714</color>
     <color name="blocked_number_primary_text_color">@color/dialtacts_primary_text_color</color>
     <color name="blocked_number_secondary_text_color">@color/dialtacts_secondary_text_color</color>
     <color name="blocked_number_icon_tint">#616161</color>
diff --git a/src/com/android/dialer/service/ExtendedCallInfoService.java b/src/com/android/dialer/service/ExtendedCallInfoService.java
new file mode 100644
index 0000000..33a85f4
--- /dev/null
+++ b/src/com/android/dialer/service/ExtendedCallInfoService.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2016 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.service;
+
+/**
+ * Interface of service to get extended call information.
+ */
+public interface ExtendedCallInfoService {
+
+    /**
+     * Interface for a callback to be invoked when data is fetched.
+     */
+    interface Listener {
+        /**
+         * Called when data is fetched.
+         * @param isSpam True if the call is spam.
+         */
+        void onComplete(boolean isSpam);
+    }
+
+    /**
+     * Gets extended call information.
+     * @param number The phone number of the call.
+     * @param listener The callback to be invoked after {@code Info} is fetched.
+     */
+    void getExtendedCallInfo(String number, Listener listener);
+}
diff --git a/src/com/android/dialerbind/ObjectFactory.java b/src/com/android/dialerbind/ObjectFactory.java
index 303610f..4703347 100644
--- a/src/com/android/dialerbind/ObjectFactory.java
+++ b/src/com/android/dialerbind/ObjectFactory.java
@@ -27,6 +27,7 @@
 import com.android.dialer.logging.Logger;
 import com.android.dialer.service.CachedNumberLookupService;
 import com.android.dialer.service.ExtendedBlockingButtonRenderer;
+import com.android.dialer.service.ExtendedCallInfoService;
 import com.android.dialer.voicemail.VoicemailPlaybackPresenter;
 
 /**
@@ -65,6 +66,11 @@
         return null;
     }
 
+    @Nullable
+    public static ExtendedCallInfoService newExtendedCallInfoService(Context context) {
+        return null;
+    }
+
     /**
      * Create a new instance of the call log adapter.
      * @param context The context to use.