Merge "PhoneStateListener & ContentObserver for blocked calls." into ub-contactsdialer-a-dev
diff --git a/InCallUI/src/com/android/incallui/Call.java b/InCallUI/src/com/android/incallui/Call.java
index c5971ff..e14db9b 100644
--- a/InCallUI/src/com/android/incallui/Call.java
+++ b/InCallUI/src/com/android/incallui/Call.java
@@ -368,6 +368,8 @@
     private String mLastForwardedNumber;
     private String mCallSubject;
 
+    private long mTimeAddedMs;
+
     private LogState mLogState = new LogState();
 
     /**
@@ -386,6 +388,8 @@
 
         updateFromTelecomCall();
         mTelecomCall.registerCallback(mTelecomCallCallback);
+
+        mTimeAddedMs = System.currentTimeMillis();
     }
 
     public android.telecom.Call getTelecomCall() {
@@ -513,6 +517,10 @@
         return mId;
     }
 
+    public long getTimeAddedMs() {
+        return mTimeAddedMs;
+    }
+
     public String getNumber() {
         if (mTelecomCall == null) {
             return null;
@@ -525,7 +533,6 @@
     }
 
     public void blockCall() {
-        // TODO: Some vibration still occurs.
         mTelecomCall.reject(false, null);
         setState(State.BLOCKED);
     }
diff --git a/InCallUI/src/com/android/incallui/CallList.java b/InCallUI/src/com/android/incallui/CallList.java
index 231df06..4f9e0af 100644
--- a/InCallUI/src/com/android/incallui/CallList.java
+++ b/InCallUI/src/com/android/incallui/CallList.java
@@ -89,6 +89,7 @@
         Trace.beginSection("onCallAdded");
         final Call call = new Call(telecomCall);
         Log.d(this, "onCallAdded: callState=" + call.getState());
+
         // Check if call should be blocked.
         if (!call.isEmergencyCall() && call.getState() == Call.State.INCOMING) {
             final AtomicBoolean hasTimedOut = new AtomicBoolean(false);
@@ -118,6 +119,10 @@
                                 call.blockCall();
                                 Log.d(this, "onCallAdded: "
                                         + Log.pii(call.getNumber()) + " blocked.");
+
+                                // Call back to the presenter so it can update the call log.
+                                InCallPresenter.getInstance().onCallBlocked(
+                                        call.getNumber(), call.getTimeAddedMs());
                             }
                         }
                     }, null, call.getNumber(), countryIso)) {
diff --git a/InCallUI/src/com/android/incallui/InCallPresenter.java b/InCallUI/src/com/android/incallui/InCallPresenter.java
index 032d096..d61a0ba 100644
--- a/InCallUI/src/com/android/incallui/InCallPresenter.java
+++ b/InCallUI/src/com/android/incallui/InCallPresenter.java
@@ -22,24 +22,34 @@
 import android.content.Intent;
 import android.content.pm.ActivityInfo;
 import android.content.res.Resources;
+import android.database.ContentObserver;
 import android.graphics.Point;
 import android.net.Uri;
 import android.os.Bundle;
+import android.os.Handler;
+import android.provider.CallLog;
 import android.telecom.DisconnectCause;
 import android.telecom.PhoneAccount;
 import android.telecom.PhoneAccountHandle;
 import android.telecom.TelecomManager;
 import android.telecom.VideoProfile;
 import android.telephony.PhoneNumberUtils;
+import android.telephony.PhoneStateListener;
+import android.telephony.TelephonyManager;
 import android.text.TextUtils;
 import android.view.Surface;
 import android.view.View;
 import android.view.Window;
 import android.view.WindowManager;
 
+import com.android.contacts.common.GeoUtil;
 import com.android.contacts.common.interactions.TouchPointManager;
 import com.android.contacts.common.testing.NeededForTesting;
 import com.android.contacts.common.util.MaterialColorMapUtils.MaterialPalette;
+import com.android.dialer.calllog.CallLogAsyncTaskUtil;
+import com.android.dialer.calllog.CallLogAsyncTaskUtil.OnCallLogQueryFinishedListener;
+import com.android.dialer.database.FilteredNumberAsyncQueryHandler;
+import com.android.dialer.database.FilteredNumberAsyncQueryHandler.OnCheckBlockedListener;
 import com.android.incalluibind.ObjectFactory;
 import com.google.common.base.Preconditions;
 
@@ -100,6 +110,7 @@
     private boolean mAccountSelectionCancelled = false;
     private InCallCameraManager mInCallCameraManager = null;
     private AnswerPresenter mAnswerPresenter = new AnswerPresenter();
+    private FilteredNumberAsyncQueryHandler mFilteredQueryHandler;
 
     /**
      * Whether or not we are currently bound and waiting for Telecom to send us a new call.
@@ -151,6 +162,80 @@
         }
     };
 
+    private PhoneStateListener mPhoneStateListener = new PhoneStateListener() {
+        public void onCallStateChanged(int state, String incomingNumber) {
+            if (state == TelephonyManager.CALL_STATE_RINGING) {
+                // Check if the number is blocked, to silence the ringer.
+                String countryIso = GeoUtil.getCurrentCountryIso(mContext);
+                mFilteredQueryHandler.startBlockedQuery(
+                        mOnCheckBlockedListener, null, incomingNumber, countryIso);
+            }
+        }
+    };
+
+    private final OnCheckBlockedListener mOnCheckBlockedListener = new OnCheckBlockedListener() {
+        @Override
+        public void onCheckComplete(final Integer id) {
+            if (id != null) {
+                // Silence the ringer now to prevent ringing and vibration before the call is
+                // terminated when Telecom attempts to add it.
+                getTelecomManager().silenceRinger();
+            }
+        }
+    };
+
+    /**
+     * Observes the CallLog for changes so that when call log entries for blocked calls are added
+     * they can be marked with the blocked call type. Times out if too much time has passed.
+     */
+    private class BlockedNumberContentObserver extends ContentObserver {
+        private static final int TIMEOUT_MS = 5000;
+
+        private Handler mHandler;
+        private String mNumber;
+        private long mTimeAddedMs;
+
+        public BlockedNumberContentObserver(Handler handler, String number, long timeAddedMs) {
+            super(handler);
+
+            mHandler = handler;
+            mNumber = number;
+            mTimeAddedMs = timeAddedMs;
+        }
+
+        @Override
+        public void onChange(boolean selfChange) {
+            CallLogAsyncTaskUtil.markCallAsBlocked(mContext, mNumber, mTimeAddedMs,
+                    new OnCallLogQueryFinishedListener() {
+                        @Override
+                        public void onQueryFinished(boolean hasEntry) {
+                            if (mContext != null && hasEntry) {
+                                unregister();
+                            }
+                        }
+                    });
+        }
+
+        public void register() {
+            if (mContext != null) {
+                mContext.getContentResolver().registerContentObserver(
+                        CallLog.CONTENT_URI, true, this);
+                mHandler.postDelayed(new Runnable() {
+                    @Override
+                    public void run() {
+                        unregister();
+                    }
+                }, TIMEOUT_MS);
+            }
+        }
+
+        private void unregister() {
+            if (mContext != null) {
+                mContext.getContentResolver().unregisterContentObserver(this);
+            }
+        }
+    };
+
     /**
      * Is true when the activity has been previously started. Some code needs to know not just if
      * the activity is currently up, but if it had been previously shown in foreground for this
@@ -176,6 +261,7 @@
     private MaterialPalette mThemeColors;
 
     private TelecomManager mTelecomManager;
+    private TelephonyManager mTelephonyManager;
 
     public static synchronized InCallPresenter getInstance() {
         if (sInCallPresenter == null) {
@@ -239,6 +325,11 @@
 
         VideoPauseController.getInstance().setUp(this);
 
+        mFilteredQueryHandler = new FilteredNumberAsyncQueryHandler(context.getContentResolver());
+        mTelephonyManager = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
+        mTelephonyManager.listen(mPhoneStateListener, PhoneStateListener.LISTEN_CALL_STATE);
+        mCallList.setFilteredNumberQueryHandler(mFilteredQueryHandler);
+
         Log.d(this, "Finished InCallPresenter.setUp");
     }
 
@@ -255,6 +346,7 @@
         mServiceConnected = false;
         attemptCleanup();
 
+        mTelephonyManager.listen(mPhoneStateListener, PhoneStateListener.LISTEN_NONE);
         VideoPauseController.getInstance().tearDown();
     }
 
@@ -391,9 +483,8 @@
      * TODO: Consider listening to CallList callbacks to do this instead of receiving a direct
      * method invocation from InCallService.
      */
-    public void onCallAdded(android.telecom.Call call) {
-        // Since a call has been added we are no longer waiting for Telecom to send us a
-        // call.
+    public void onCallAdded(final android.telecom.Call call) {
+        // Since a call has been added we are no longer waiting for Telecom to send us a call.
         setBoundAndWaitingForOutgoingCall(false, null);
         call.registerCallback(mCallCallback);
     }
@@ -492,6 +583,14 @@
         }
     }
 
+    public void onCallBlocked(String number, long timeAddedMs) {
+        BlockedNumberContentObserver contentObserver =
+                new BlockedNumberContentObserver(new Handler(), number, timeAddedMs);
+
+        // BlockedNumberContentObserver will unregister after successful log or timeout.
+        contentObserver.register();
+    }
+
     /**
      * Given the call list, return the state in which the in-call screen should be.
      */
diff --git a/InCallUI/src/com/android/incallui/InCallServiceImpl.java b/InCallUI/src/com/android/incallui/InCallServiceImpl.java
index 6078d3d..ee2ca1b 100644
--- a/InCallUI/src/com/android/incallui/InCallServiceImpl.java
+++ b/InCallUI/src/com/android/incallui/InCallServiceImpl.java
@@ -23,7 +23,7 @@
 import android.telecom.CallAudioState;
 import android.telecom.InCallService;
 
-import com.android.contacts.common.util.TelephonyManagerUtils;
+import com.android.contacts.common.GeoUtil;
 import com.android.dialer.database.FilteredNumberAsyncQueryHandler;
 
 import java.util.Locale;
@@ -48,9 +48,7 @@
 
     @Override
     public void onCallAdded(Call call) {
-        final String countryIso = TelephonyManagerUtils.getCurrentCountryIso(
-                getApplicationContext(),
-                Locale.getDefault());
+        final String countryIso = GeoUtil.getCurrentCountryIso(getApplicationContext());
         CallList.getInstance().onCallAdded(call, countryIso);
         InCallPresenter.getInstance().onCallAdded(call);
     }