summaryrefslogtreecommitdiff
path: root/InCallUI
diff options
context:
space:
mode:
authorSarmad Hashmi <mhashmi@google.com>2016-04-09 22:18:12 -0700
committerSarmad Hashmi <mhashmi@google.com>2016-04-12 16:30:47 -0700
commit0839b1eccc8e373e01d96cc97f079b1ce44b5d33 (patch)
treee4b11f8917dd64e8ee26851fb6dc186c0be94628 /InCallUI
parent215e1f189805fbc3dd30e084b715d5e219852902 (diff)
Add after call notification code in InCallUI.
+Add asynchronous call to update inCallHistory boolean variable which specifies whether the number is in the call history (has previously called before) +Add CallHistoryStatus enum and variable to Call object to determine if the number is present in the call history +Added SpamCallListListener object which listens for changes in the CallList +Update the CallHistoryStatus for the call whenever an incoming call comes in +Added 11 tests for new listener BUG=27323295 Change-Id: I242cd4a53b3aeca69fbce972221a2c941d9d37ce
Diffstat (limited to 'InCallUI')
-rw-r--r--InCallUI/src/com/android/incallui/Call.java45
-rw-r--r--InCallUI/src/com/android/incallui/InCallPresenter.java7
-rw-r--r--InCallUI/src/com/android/incallui/spam/SpamCallListListener.java117
-rw-r--r--InCallUI/tests/src/com/android/incallui/spam/SpamCallListListenerTest.java206
4 files changed, 365 insertions, 10 deletions
diff --git a/InCallUI/src/com/android/incallui/Call.java b/InCallUI/src/com/android/incallui/Call.java
index a27efec76..252c7016d 100644
--- a/InCallUI/src/com/android/incallui/Call.java
+++ b/InCallUI/src/com/android/incallui/Call.java
@@ -21,6 +21,7 @@ import android.hardware.camera2.CameraCharacteristics;
import android.net.Uri;
import android.os.Bundle;
import android.os.Trace;
+import android.support.annotation.IntDef;
import android.telecom.Call.Details;
import android.telecom.Connection;
import android.telecom.DisconnectCause;
@@ -41,6 +42,8 @@ import com.android.contacts.common.testing.NeededForTesting;
import com.android.dialer.util.IntentUtil;
import com.android.incallui.util.TelecomCallUtil;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
@@ -51,6 +54,19 @@ import java.util.Objects;
*/
@NeededForTesting
public class Call {
+
+ /**
+ * Specifies whether a number is in the call history or not.
+ * {@link #CALL_HISTORY_STATUS_UNKNOWN} means there is no result.
+ */
+ @IntDef({CALL_HISTORY_STATUS_UNKNOWN, CALL_HISTORY_STATUS_PRESENT,
+ CALL_HISTORY_STATUS_NOT_PRESENT})
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface CallHistoryStatus {}
+ public static final int CALL_HISTORY_STATUS_UNKNOWN = 0;
+ public static final int CALL_HISTORY_STATUS_PRESENT = 1;
+ public static final int CALL_HISTORY_STATUS_NOT_PRESENT = 2;
+
/* Defines different states of this call */
public static class State {
public static final int INVALID = 0;
@@ -381,6 +397,8 @@ public class Call {
private String mLastForwardedNumber;
private String mCallSubject;
private PhoneAccountHandle mPhoneAccountHandle;
+ @CallHistoryStatus private int mCallHistoryStatus = CALL_HISTORY_STATUS_UNKNOWN;
+ private boolean mIsSpam;
/**
* Indicates whether the phone account associated with this call supports specifying a call
@@ -392,16 +410,6 @@ public class Call {
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
*/
@@ -987,4 +995,21 @@ public class Call {
public String toSimpleString() {
return super.toString();
}
+
+ public void setCallHistoryStatus(@CallHistoryStatus int callHistoryStatus) {
+ mCallHistoryStatus = callHistoryStatus;
+ }
+
+ @CallHistoryStatus
+ public int getCallHistoryStatus() {
+ return mCallHistoryStatus;
+ }
+
+ public void setSpam(boolean isSpam) {
+ mIsSpam = isSpam;
+ }
+
+ public boolean isSpam() {
+ return mIsSpam;
+ }
}
diff --git a/InCallUI/src/com/android/incallui/InCallPresenter.java b/InCallUI/src/com/android/incallui/InCallPresenter.java
index 189049c47..4442cbcd2 100644
--- a/InCallUI/src/com/android/incallui/InCallPresenter.java
+++ b/InCallUI/src/com/android/incallui/InCallPresenter.java
@@ -56,6 +56,7 @@ import com.android.dialer.filterednumber.FilteredNumbersUtil;
import com.android.dialer.logging.InteractionEvent;
import com.android.dialer.logging.Logger;
import com.android.dialer.util.TelecomUtil;
+import com.android.incallui.spam.SpamCallListListener;
import com.android.incallui.util.TelecomCallUtil;
import com.android.incalluibind.ObjectFactory;
@@ -123,6 +124,7 @@ public class InCallPresenter implements CallList.Listener,
private InCallCameraManager mInCallCameraManager = null;
private AnswerPresenter mAnswerPresenter = new AnswerPresenter();
private FilteredNumberAsyncQueryHandler mFilteredQueryHandler;
+ private CallList.Listener mSpamCallListListener;
/**
* Whether or not we are currently bound and waiting for Telecom to send us a new call.
@@ -345,6 +347,10 @@ public class InCallPresenter implements CallList.Listener,
// will kick off an update and the whole process can start.
mCallList.addListener(this);
+ // Create spam call list listener and add it to the list of listeners
+ mSpamCallListListener = new SpamCallListListener(context);
+ mCallList.addListener(mSpamCallListListener);
+
VideoPauseController.getInstance().setUp(this);
InCallVideoCallCallbackNotifier.getInstance().addSessionModificationListener(this);
@@ -1532,6 +1538,7 @@ public class InCallPresenter implements CallList.Listener,
if (mCallList != null) {
mCallList.removeListener(this);
+ mCallList.removeListener(mSpamCallListListener);
}
mCallList = null;
diff --git a/InCallUI/src/com/android/incallui/spam/SpamCallListListener.java b/InCallUI/src/com/android/incallui/spam/SpamCallListListener.java
new file mode 100644
index 000000000..b97f4d099
--- /dev/null
+++ b/InCallUI/src/com/android/incallui/spam/SpamCallListListener.java
@@ -0,0 +1,117 @@
+/*
+ * 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.incallui.spam;
+
+import com.google.common.annotations.VisibleForTesting;
+
+import android.content.Context;
+import android.telecom.DisconnectCause;
+import android.text.TextUtils;
+
+import com.android.dialer.calllog.CallLogAsyncTaskUtil;
+import com.android.incallui.Call;
+import com.android.incallui.CallList;
+import com.android.incallui.Log;
+
+public class SpamCallListListener implements CallList.Listener {
+ private static final String TAG = "SpamCallListListener";
+
+ private final Context mContext;
+
+ public SpamCallListListener(Context context) {
+ mContext = context;
+ }
+
+ @Override
+ public void onIncomingCall(final Call call) {
+ String number = call.getNumber();
+ if (TextUtils.isEmpty(number)) {
+ return;
+ }
+ CallLogAsyncTaskUtil.getNumberInCallHistory(mContext, number,
+ new CallLogAsyncTaskUtil.OnGetNumberInCallHistoryListener() {
+ @Override
+ public void onComplete(boolean inCallHistory) {
+ call.setCallHistoryStatus(inCallHistory ?
+ Call.CALL_HISTORY_STATUS_PRESENT
+ : Call.CALL_HISTORY_STATUS_NOT_PRESENT);
+ }
+ });
+ }
+
+ @Override
+ public void onUpgradeToVideo(Call call) {}
+
+ @Override
+ public void onCallListChange(CallList callList) {}
+
+ @Override
+ public void onDisconnect(Call call) {
+ if (shouldShowAfterCallNotification(call)) {
+ showNotification(call.getNumber());
+ }
+ }
+
+ /**
+ * Posts the intent for displaying the after call spam notification to the user.
+ */
+ @VisibleForTesting
+ /* package */ void showNotification(String number) {
+ //TODO(mhashmi): build and show notifications here
+ }
+
+ /**
+ * Determines if the after call notification should be shown for the specified call.
+ */
+ private boolean shouldShowAfterCallNotification(Call call) {
+ String number = call.getNumber();
+ if (TextUtils.isEmpty(number)) {
+ return false;
+ }
+
+ Call.LogState logState = call.getLogState();
+ if (!logState.isIncoming) {
+ return false;
+ }
+
+ if (logState.duration <= 0) {
+ return false;
+ }
+
+ if (logState.contactLookupResult != Call.LogState.LOOKUP_NOT_FOUND
+ && logState.contactLookupResult != Call.LogState.LOOKUP_UNKNOWN) {
+ return false;
+ }
+
+ int callHistoryStatus = call.getCallHistoryStatus();
+ if (callHistoryStatus == Call.CALL_HISTORY_STATUS_PRESENT) {
+ return false;
+ } else if (callHistoryStatus == Call.CALL_HISTORY_STATUS_UNKNOWN) {
+ Log.i(TAG, "Call history status is unknown, returning false");
+ return false;
+ }
+
+ // Check if call disconnected because of either user hanging up
+ int disconnectCause = call.getDisconnectCause().getCode();
+ if (disconnectCause != DisconnectCause.LOCAL && disconnectCause != DisconnectCause.REMOTE) {
+ return false;
+ }
+
+ Log.i(TAG, "shouldShowAfterCallNotification, returning true");
+ return true;
+ }
+} \ No newline at end of file
diff --git a/InCallUI/tests/src/com/android/incallui/spam/SpamCallListListenerTest.java b/InCallUI/tests/src/com/android/incallui/spam/SpamCallListListenerTest.java
new file mode 100644
index 000000000..fb0b460b6
--- /dev/null
+++ b/InCallUI/tests/src/com/android/incallui/spam/SpamCallListListenerTest.java
@@ -0,0 +1,206 @@
+/*
+ * 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.incallui.spam;
+
+import static org.mockito.Matchers.anyInt;
+import static org.mockito.Mockito.doCallRealMethod;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.eq;
+import static org.mockito.Mockito.any;
+
+import android.content.ContentValues;
+import android.content.Context;
+import android.provider.CallLog;
+import android.telecom.DisconnectCause;
+import android.test.InstrumentationTestCase;
+
+import com.android.contacts.common.test.mocks.ContactsMockContext;
+import com.android.contacts.common.test.mocks.MockContentProvider;
+import com.android.dialer.calllog.CallLogAsyncTaskUtil;
+import com.android.dialer.util.AsyncTaskExecutors;
+import com.android.dialer.util.FakeAsyncTaskExecutor;
+import com.android.dialer.util.TelecomUtil;
+import com.android.incallui.Call;
+import com.android.incallui.Call.CallHistoryStatus;
+import com.android.incallui.Call.LogState;
+
+public class SpamCallListListenerTest extends InstrumentationTestCase {
+ private static final String NUMBER = "+18005657862";
+ private static final int DURATION = 100;
+
+ private TestSpamCallListListener mListener;
+ private FakeAsyncTaskExecutor mFakeAsyncTaskExecutor;
+ private ContactsMockContext mContext;
+
+ @Override
+ public void setUp() throws Exception {
+ super.setUp();
+ mContext = new ContactsMockContext(getInstrumentation().getContext(), CallLog.AUTHORITY);
+ mListener = new TestSpamCallListListener(mContext);
+ mFakeAsyncTaskExecutor = new FakeAsyncTaskExecutor(getInstrumentation());
+ AsyncTaskExecutors.setFactoryForTest(mFakeAsyncTaskExecutor.getFactory());
+ }
+
+ @Override
+ public void tearDown() throws Exception {
+ AsyncTaskExecutors.setFactoryForTest(null);
+ CallLogAsyncTaskUtil.resetForTest();
+ super.tearDown();
+ }
+
+ public void testOutgoingCall() {
+ Call call = getMockCall(NUMBER, false, 0, Call.CALL_HISTORY_STATUS_NOT_PRESENT,
+ LogState.LOOKUP_UNKNOWN, DisconnectCause.REMOTE);
+ mListener.onDisconnect(call);
+ assertFalse(mListener.mShowNotificationCalled);
+ }
+
+ public void testIncomingCall_UnknownNumber() {
+ Call call = getMockCall(null, true, DURATION, Call.CALL_HISTORY_STATUS_NOT_PRESENT,
+ LogState.LOOKUP_UNKNOWN, DisconnectCause.REMOTE);
+ mListener.onDisconnect(call);
+ assertFalse(mListener.mShowNotificationCalled);
+ }
+
+ public void testIncomingCall_Rejected() {
+ Call call = getMockCall(NUMBER, true, 0, Call.CALL_HISTORY_STATUS_NOT_PRESENT,
+ LogState.LOOKUP_UNKNOWN, DisconnectCause.REJECTED);
+ mListener.onDisconnect(call);
+ assertFalse(mListener.mShowNotificationCalled);
+ }
+ public void testIncomingCall_HangUpLocal() {
+ Call call = getMockCall(NUMBER, true, DURATION, Call.CALL_HISTORY_STATUS_NOT_PRESENT,
+ LogState.LOOKUP_UNKNOWN, DisconnectCause.LOCAL);
+ mListener.onDisconnect(call);
+ assertTrue(mListener.mShowNotificationCalled);
+ }
+
+ public void testIncomingCall_HangUpRemote() {
+ Call call = getMockCall(NUMBER, true, DURATION, Call.CALL_HISTORY_STATUS_NOT_PRESENT,
+ LogState.LOOKUP_UNKNOWN, DisconnectCause.REMOTE);
+ mListener.onDisconnect(call);
+ assertTrue(mListener.mShowNotificationCalled);
+ }
+
+ public void testIncomingCall_ValidNumber_NotInCallHistory_InContacts() {
+ Call call = getMockCall(NUMBER, true, 0, Call.CALL_HISTORY_STATUS_NOT_PRESENT,
+ LogState.LOOKUP_LOCAL_CONTACT, DisconnectCause.REJECTED);
+ mListener.onDisconnect(call);
+ assertFalse(mListener.mShowNotificationCalled);
+ }
+
+ public void testIncomingCall_ValidNumber_InCallHistory_InContacts() {
+ Call call = getMockCall(NUMBER, true, 0, Call.CALL_HISTORY_STATUS_PRESENT,
+ LogState.LOOKUP_LOCAL_CONTACT, DisconnectCause.REJECTED);
+ mListener.onDisconnect(call);
+ assertFalse(mListener.mShowNotificationCalled);
+ }
+
+ public void testIncomingCall_ValidNumber_InCallHistory_NotInContacts() {
+ Call call = getMockCall(NUMBER, true, 0, Call.CALL_HISTORY_STATUS_PRESENT,
+ LogState.LOOKUP_UNKNOWN, DisconnectCause.REJECTED);
+ mListener.onDisconnect(call);
+ assertFalse(mListener.mShowNotificationCalled);
+ }
+
+ public void testIncomingCall_ValidNumber_NotInCallHistory_NotInContacts() throws Throwable {
+ Call call = getMockCall(NUMBER, true, DURATION, Call.CALL_HISTORY_STATUS_NOT_PRESENT,
+ LogState.LOOKUP_UNKNOWN, DisconnectCause.LOCAL);
+ mListener.onDisconnect(call);
+ assertTrue(mListener.mShowNotificationCalled);
+ }
+
+ public void testIncomingCall_CheckCallHistory_NumberExists() throws Throwable {
+ final Call call = getMockCall(NUMBER, true, DURATION, Call.CALL_HISTORY_STATUS_UNKNOWN,
+ LogState.LOOKUP_UNKNOWN, DisconnectCause.LOCAL);
+ expectCallLogQuery(NUMBER, true);
+ incomingCall(call);
+ verify(call).setCallHistoryStatus(eq(Call.CALL_HISTORY_STATUS_PRESENT));
+ assertFalse(mListener.mShowNotificationCalled);
+ }
+
+ public void testIncomingCall_CheckCallHistory_NumberNotExists() throws Throwable {
+ final Call call = getMockCall(NUMBER, true, DURATION, Call.CALL_HISTORY_STATUS_UNKNOWN,
+ LogState.LOOKUP_UNKNOWN, DisconnectCause.LOCAL);
+ expectCallLogQuery(NUMBER, false);
+ incomingCall(call);
+ verify(call).setCallHistoryStatus(eq(Call.CALL_HISTORY_STATUS_NOT_PRESENT));
+ assertTrue(mListener.mShowNotificationCalled);
+ }
+
+ private void incomingCall(final Call call) throws Throwable {
+ runTestOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ mListener.onIncomingCall(call);
+ }
+ });
+ getInstrumentation().waitForIdleSync();
+ mFakeAsyncTaskExecutor.runTask(CallLogAsyncTaskUtil.Tasks.GET_NUMBER_IN_CALL_HISTORY);
+ mListener.onDisconnect(call);
+ }
+
+ private void expectCallLogQuery(String number, boolean inCallHistory) {
+ MockContentProvider.Query query = mContext.getContactsProvider()
+ .expectQuery(TelecomUtil.getCallLogUri(mContext))
+ .withSelection(CallLog.Calls.NUMBER + " = ?", number)
+ .withProjection(CallLog.Calls._ID)
+ .withAnySortOrder();
+ ContentValues values = new ContentValues();
+ values.put(CallLog.Calls.NUMBER, number);
+ if (inCallHistory) {
+ query.returnRow(values);
+ } else {
+ query.returnEmptyCursor();
+ }
+ }
+
+ private static Call getMockCall(String number,
+ boolean isIncoming,
+ int duration,
+ @CallHistoryStatus int callHistoryStatus,
+ int contactLookupResult,
+ int disconnectCause) {
+ Call call = mock(Call.class);
+ LogState logState = new LogState();
+ logState.isIncoming = isIncoming;
+ logState.duration = duration;
+ logState.contactLookupResult = contactLookupResult;
+ when(call.getDisconnectCause()).thenReturn(new DisconnectCause(disconnectCause));
+ when(call.getLogState()).thenReturn(logState);
+ when(call.getNumber()).thenReturn(number);
+ doCallRealMethod().when(call).setCallHistoryStatus(anyInt());
+ when(call.getCallHistoryStatus()).thenCallRealMethod();
+ call.setCallHistoryStatus(callHistoryStatus);
+ return call;
+ }
+
+ private static class TestSpamCallListListener extends SpamCallListListener {
+ private boolean mShowNotificationCalled;
+
+ public TestSpamCallListListener(Context context) {
+ super(context);
+ }
+
+ void showNotification(String number) {
+ mShowNotificationCalled = true;
+ }
+ }
+} \ No newline at end of file