diff options
Diffstat (limited to 'InCallUI/src/com/android')
11 files changed, 426 insertions, 31 deletions
diff --git a/InCallUI/src/com/android/incallui/Call.java b/InCallUI/src/com/android/incallui/Call.java index d552ecfe5..1ad37e01a 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; @@ -359,7 +375,8 @@ public class Call { } }; - private android.telecom.Call mTelecomCall; + private final android.telecom.Call mTelecomCall; + private final LatencyReport mLatencyReport; private boolean mIsEmergencyCall; private Uri mHandle; private final String mId; @@ -381,6 +398,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 @@ -390,7 +409,7 @@ public class Call { private long mTimeAddedMs; - private LogState mLogState = new LogState(); + private final LogState mLogState = new LogState(); /** * Used only to create mock calls for testing @@ -398,6 +417,7 @@ public class Call { @NeededForTesting Call(int state) { mTelecomCall = null; + mLatencyReport = new LatencyReport(); mId = ID_PREFIX + Integer.toString(sIdCounter++); setState(state); } @@ -406,8 +426,8 @@ public class Call { * Creates a new instance of a {@link Call}. Registers a callback for * {@link android.telecom.Call} events. */ - public Call(android.telecom.Call telecomCall) { - this(telecomCall, true /* registerCallback */); + public Call(android.telecom.Call telecomCall, LatencyReport latencyReport) { + this(telecomCall, latencyReport, true /* registerCallback */); } /** @@ -417,8 +437,10 @@ public class Call { * Intended for use when creating a {@link Call} instance for use with the * {@link ContactInfoCache}, where we do not want to register callbacks for the new call. */ - public Call(android.telecom.Call telecomCall, boolean registerCallback) { + public Call(android.telecom.Call telecomCall, LatencyReport latencyReport, + boolean registerCallback) { mTelecomCall = telecomCall; + mLatencyReport = latencyReport; mId = ID_PREFIX + Integer.toString(sIdCounter++); updateFromTelecomCall(registerCallback); @@ -977,4 +999,25 @@ 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; + } + + public LatencyReport getLatencyReport() { + return mLatencyReport; + } } diff --git a/InCallUI/src/com/android/incallui/CallCardFragment.java b/InCallUI/src/com/android/incallui/CallCardFragment.java index 39dd5eae2..506feb572 100644 --- a/InCallUI/src/com/android/incallui/CallCardFragment.java +++ b/InCallUI/src/com/android/incallui/CallCardFragment.java @@ -131,6 +131,7 @@ public class CallCardFragment extends BaseFragment<CallCardPresenter, CallCardPr private TextView mCallTypeLabel; private ImageView mHdAudioIcon; private ImageView mForwardIcon; + private ImageView mSpamIcon; private View mCallNumberAndLabel; private TextView mElapsedTime; private Drawable mPrimaryPhotoDrawable; @@ -281,6 +282,7 @@ public class CallCardFragment extends BaseFragment<CallCardPresenter, CallCardPr 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); @@ -1216,6 +1218,19 @@ public class CallCardFragment extends BaseFragment<CallCardPresenter, CallCardPr 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 10bf5e612..3761c5a7f 100644 --- a/InCallUI/src/com/android/incallui/CallCardPresenter.java +++ b/InCallUI/src/com/android/incallui/CallCardPresenter.java @@ -458,6 +458,10 @@ public class CallCardPresenter extends Presenter<CallCardPresenter.CallCardUi> 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 @@ public class CallCardPresenter extends Presenter<CallCardPresenter.CallCardUi> boolean isEmergencyCall = mPrimary.isEmergencyCall(); mEmergencyCallListener.onCallUpdated((BaseFragment) ui, isEmergencyCall); } + maybeShowSpamIconAndLabel(); } private void updateSecondaryDisplayInfo() { @@ -1163,6 +1168,7 @@ public class CallCardPresenter extends Presenter<CallCardPresenter.CallCardUi> 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 d0f3c1000..48870f68a 100644 --- a/InCallUI/src/com/android/incallui/CallList.java +++ b/InCallUI/src/com/android/incallui/CallList.java @@ -23,8 +23,8 @@ import android.telecom.DisconnectCause; 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 @@ public class CallList { .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. @@ -86,14 +86,25 @@ public class CallList { CallList() { } - public void onCallAdded(final android.telecom.Call telecomCall) { + public void onCallAdded(final android.telecom.Call telecomCall, LatencyReport latencyReport) { Trace.beginSection("onCallAdded"); - final Call call = new Call(telecomCall); + final Call call = new Call(telecomCall, latencyReport); Log.d(this, "onCallAdded: callState=" + call.getState()); 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, null, + new ExtendedCallInfoService.Listener() { + @Override + public void onComplete(boolean isSpam) { + call.setSpam(isSpam); + onUpdate(call); + } + }); + } } else { onUpdate(call); } @@ -615,8 +626,14 @@ public class CallList { } }; - public void setFilteredNumberQueryHandler(FilteredNumberAsyncQueryHandler handler) { - mFilteredQueryHandler = handler; + public void setExtendedCallInfoService(ExtendedCallInfoService service) { + mExtendedCallInfoService = service; + } + + public void onInCallUiShown(boolean forFullScreenIntent) { + for (Call call : mCallById.values()) { + call.getLatencyReport().onInCallUiShown(forFullScreenIntent); + } } /** diff --git a/InCallUI/src/com/android/incallui/CallerInfo.java b/InCallUI/src/com/android/incallui/CallerInfo.java index f270678e0..f3d0e0763 100644 --- a/InCallUI/src/com/android/incallui/CallerInfo.java +++ b/InCallUI/src/com/android/incallui/CallerInfo.java @@ -322,7 +322,7 @@ public class CallerInfo { info.userType = ContactsUtils.determineUserType(directoryId, contactId); info.nameAlternative = ContactInfoHelper.lookUpDisplayNameAlternative( - context, info.lookupKeyOrNull, info.userType); + context, info.lookupKeyOrNull, info.userType, directoryId); } cursor.close(); } diff --git a/InCallUI/src/com/android/incallui/ExternalCallNotifier.java b/InCallUI/src/com/android/incallui/ExternalCallNotifier.java index 40a2e02bf..639a46da0 100644 --- a/InCallUI/src/com/android/incallui/ExternalCallNotifier.java +++ b/InCallUI/src/com/android/incallui/ExternalCallNotifier.java @@ -182,7 +182,7 @@ public class ExternalCallNotifier implements ExternalCallList.ExternalCallListen // it has available, and may make a subsequent call later (same thread) if it had to // call into the contacts provider for more data. com.android.incallui.Call incallCall = new com.android.incallui.Call(info.getCall(), - false /* registerCallback */); + new LatencyReport(), false /* registerCallback */); mContactInfoCache.findInfo(incallCall, false /* isIncoming */, new ContactInfoCache.ContactInfoCacheCallback() { diff --git a/InCallUI/src/com/android/incallui/InCallActivity.java b/InCallUI/src/com/android/incallui/InCallActivity.java index eca79f8a7..5ae231ae1 100644 --- a/InCallUI/src/com/android/incallui/InCallActivity.java +++ b/InCallUI/src/com/android/incallui/InCallActivity.java @@ -77,6 +77,7 @@ public class InCallActivity extends TransactionSafeActivity implements FragmentD public static final String SHOW_DIALPAD_EXTRA = "InCallActivity.show_dialpad"; public static final String DIALPAD_TEXT_EXTRA = "InCallActivity.dialpad_text"; public static final String NEW_OUTGOING_CALL_EXTRA = "InCallActivity.new_outgoing_call"; + public static final String FOR_FULL_SCREEN_INTENT = "InCallActivity.for_full_screen_intent"; private static final String TAG_DIALPAD_FRAGMENT = "tag_dialpad_fragment"; private static final String TAG_CONFERENCE_FRAGMENT = "tag_conference_manager_fragment"; @@ -301,6 +302,9 @@ public class InCallActivity extends TransactionSafeActivity implements FragmentD if (mShowPostCharWaitDialogOnResume) { showPostCharWaitDialog(mShowPostCharWaitDialogCallId, mShowPostCharWaitDialogChars); } + + CallList.getInstance().onInCallUiShown( + getIntent().getBooleanExtra(FOR_FULL_SCREEN_INTENT, false)); } // onPause is guaranteed to be called when the InCallActivity goes diff --git a/InCallUI/src/com/android/incallui/InCallPresenter.java b/InCallUI/src/com/android/incallui/InCallPresenter.java index 38507eef9..fac539311 100644 --- a/InCallUI/src/com/android/incallui/InCallPresenter.java +++ b/InCallUI/src/com/android/incallui/InCallPresenter.java @@ -27,6 +27,7 @@ import android.database.ContentObserver; import android.graphics.Point; import android.os.Bundle; import android.os.Handler; +import android.os.SystemClock; import android.provider.CallLog; import android.telecom.DisconnectCause; import android.telecom.PhoneAccount; @@ -56,6 +57,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 +125,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,13 +348,18 @@ 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); 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"); } @@ -506,14 +514,16 @@ public class InCallPresenter implements CallList.Listener, } public void onCallAdded(final android.telecom.Call call) { + LatencyReport latencyReport = new LatencyReport(call); if (shouldAttemptBlocking(call)) { - maybeBlockCall(call); + maybeBlockCall(call, latencyReport); } else { + latencyReport.onCallBlockingDone(); if (call.getDetails() .hasProperty(CallSdkCompat.Details.PROPERTY_IS_EXTERNAL_CALL)) { mExternalCallList.onCallAdded(call); } else { - mCallList.onCallAdded(call); + mCallList.onCallAdded(call, latencyReport); } } @@ -544,7 +554,8 @@ public class InCallPresenter implements CallList.Listener, * checking whether a function is blocked does not return in a reasonable time, we proceed * with adding the call anyways. */ - private void maybeBlockCall(final android.telecom.Call call) { + private void maybeBlockCall(final android.telecom.Call call, + final LatencyReport latencyReport) { final String countryIso = GeoUtil.getCurrentCountryIso(mContext); final String number = TelecomCallUtil.getNumber(call); final long timeAdded = System.currentTimeMillis(); @@ -560,7 +571,8 @@ public class InCallPresenter implements CallList.Listener, final Runnable runnable = new Runnable() { public void run() { hasTimedOut.set(true); - mCallList.onCallAdded(call); + latencyReport.onCallBlockingDone(); + mCallList.onCallAdded(call, latencyReport); } }; handler.postDelayed(runnable, BLOCK_QUERY_TIMEOUT_MS); @@ -573,7 +585,8 @@ public class InCallPresenter implements CallList.Listener, } if (id == null) { if (!hasTimedOut.get()) { - mCallList.onCallAdded(call); + latencyReport.onCallBlockingDone(); + mCallList.onCallAdded(call, latencyReport); } } else { Log.i(this, "Rejecting incoming call from blocked number"); @@ -597,7 +610,9 @@ public class InCallPresenter implements CallList.Listener, Log.d(this, "checkForBlockedCall: invalid number, skipping block checking"); if (!hasTimedOut.get()) { handler.removeCallbacks(runnable); - mCallList.onCallAdded(call); + + latencyReport.onCallBlockingDone(); + mCallList.onCallAdded(call, latencyReport); } } } @@ -1531,6 +1546,7 @@ public class InCallPresenter implements CallList.Listener, if (mCallList != null) { mCallList.removeListener(this); + mCallList.removeListener(mSpamCallListListener); } mCallList = null; @@ -1780,7 +1796,14 @@ public class InCallPresenter implements CallList.Listener, 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/LatencyReport.java b/InCallUI/src/com/android/incallui/LatencyReport.java new file mode 100644 index 000000000..655372a8f --- /dev/null +++ b/InCallUI/src/com/android/incallui/LatencyReport.java @@ -0,0 +1,145 @@ +/* + * 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; + +import android.os.Bundle; +import android.os.SystemClock; + +import com.android.incalluibind.ObjectFactory; + +/** + * Tracks latency information for a call. + */ +public class LatencyReport { + // The following are hidden constants from android.telecom.TelecomManager. + private static final String EXTRA_CALL_CREATED_TIME_MILLIS = + "android.telecom.extra.CALL_CREATED_TIME_MILLIS"; + private static final String EXTRA_CALL_TELECOM_ROUTING_START_TIME_MILLIS = + "android.telecom.extra.CALL_TELECOM_ROUTING_START_TIME_MILLIS"; + private static final String EXTRA_CALL_TELECOM_ROUTING_END_TIME_MILLIS = + "android.telecom.extra.CALL_TELECOM_ROUTING_END_TIME_MILLIS"; + + public static final long INVALID_TIME = -1; + + private final boolean mWasIncoming; + + // Time elapsed since boot when the call was created by the connection service. + private final long mCreatedTimeMillis; + + // Time elapsed since boot when telecom began processing the call. + private final long mTelecomRoutingStartTimeMillis; + + // Time elapsed since boot when telecom finished processing the call. This includes things like + // looking up contact info and call blocking but before showing any UI. + private final long mTelecomRoutingEndTimeMillis; + + // Time elapsed since boot when the call was added to the InCallUi. + private final long mCallAddedTimeMillis; + + // Time elapsed since boot when the call was added and call blocking evaluation was completed. + private long mCallBlockingTimeMillis = INVALID_TIME; + + // Time elapsed since boot when the call notification was shown. + private long mCallNotificationTimeMillis = INVALID_TIME; + + // Time elapsed since boot when the InCallUI was shown. + private long mInCallUiShownTimeMillis = INVALID_TIME; + + // Whether the call was shown to the user as a heads up notification instead of a full screen + // UI. + private boolean mDidDisplayHeadsUpNotification; + + public LatencyReport() { + mWasIncoming = false; + mCreatedTimeMillis = INVALID_TIME; + mTelecomRoutingStartTimeMillis = INVALID_TIME; + mTelecomRoutingEndTimeMillis = INVALID_TIME; + mCallAddedTimeMillis = SystemClock.elapsedRealtime(); + } + + public LatencyReport(android.telecom.Call telecomCall) { + mWasIncoming = telecomCall.getState() == android.telecom.Call.STATE_RINGING; + Bundle extras = telecomCall.getDetails().getIntentExtras(); + if (extras == null) { + mCreatedTimeMillis = INVALID_TIME; + mTelecomRoutingStartTimeMillis = INVALID_TIME; + mTelecomRoutingEndTimeMillis = INVALID_TIME; + } else { + mCreatedTimeMillis = extras.getLong(EXTRA_CALL_CREATED_TIME_MILLIS, INVALID_TIME); + mTelecomRoutingStartTimeMillis = extras.getLong( + EXTRA_CALL_TELECOM_ROUTING_START_TIME_MILLIS, INVALID_TIME); + mTelecomRoutingEndTimeMillis = extras.getLong( + EXTRA_CALL_TELECOM_ROUTING_END_TIME_MILLIS, INVALID_TIME); + } + mCallAddedTimeMillis = SystemClock.elapsedRealtime(); + } + + public boolean getWasIncoming() { + return mWasIncoming; + } + + public long getCreatedTimeMillis() { + return mCreatedTimeMillis; + } + + public long getTelecomRoutingStartTimeMillis() { + return mTelecomRoutingStartTimeMillis; + } + + public long getTelecomRoutingEndTimeMillis() { + return mTelecomRoutingEndTimeMillis; + } + + public long getCallAddedTimeMillis() { + return mCallAddedTimeMillis; + } + + public long getCallBlockingTimeMillis() { + return mCallBlockingTimeMillis; + } + + public void onCallBlockingDone() { + if (mCallBlockingTimeMillis == INVALID_TIME) { + mCallBlockingTimeMillis = SystemClock.elapsedRealtime(); + } + } + + public long getCallNotificationTimeMillis() { + return mCallNotificationTimeMillis; + } + + public void onNotificationShown() { + if (mCallNotificationTimeMillis == INVALID_TIME) { + mCallNotificationTimeMillis = SystemClock.elapsedRealtime(); + } + } + + public long getInCallUiShownTimeMillis() { + return mInCallUiShownTimeMillis; + } + + public void onInCallUiShown(boolean forFullScreenIntent) { + if (mInCallUiShownTimeMillis == INVALID_TIME) { + mInCallUiShownTimeMillis = SystemClock.elapsedRealtime(); + mDidDisplayHeadsUpNotification = mWasIncoming && !forFullScreenIntent; + } + } + + public boolean getDidDisplayHeadsUpNotification() { + return mDidDisplayHeadsUpNotification; + } +} diff --git a/InCallUI/src/com/android/incallui/StatusBarNotifier.java b/InCallUI/src/com/android/incallui/StatusBarNotifier.java index 0662cca8d..cc87dd414 100644 --- a/InCallUI/src/com/android/incallui/StatusBarNotifier.java +++ b/InCallUI/src/com/android/incallui/StatusBarNotifier.java @@ -33,7 +33,9 @@ import android.content.Context; 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.testing.NeededForTesting; 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; @@ -76,6 +79,9 @@ public class StatusBarNotifier implements InCallPresenter.InCallStateListener, // Notification for incoming calls. This is interruptive and will show up as a HUN. private static final int NOTIFICATION_INCOMING_CALL = 2; + private static final int PENDING_INTENT_REQUEST_CODE_NON_FULL_SCREEN = 0; + private static final int PENDING_INTENT_REQUEST_CODE_FULL_SCREEN = 1; + private static final long[] VIBRATE_PATTERN = new long[] {0, 1000, 1000}; private final Context mContext; @@ -239,6 +245,10 @@ public class StatusBarNotifier implements InCallPresenter.InCallStateListener, } final int callState = call.getState(); + // Dont' show as spam if the number is in local contact. + if (contactInfo.contactLookupResult == Call.LogState.LOOKUP_LOCAL_CONTACT) { + call.setSpam(false); + } // Check if data has changed; if nothing is different, don't issue another notification. final int iconResId = getIconToDisplay(call); @@ -286,13 +296,13 @@ public class StatusBarNotifier implements InCallPresenter.InCallStateListener, builder.setPublicVersion(publicBuilder.build()); // Set up the main intent to send the user to the in-call screen - final PendingIntent inCallPendingIntent = createLaunchPendingIntent(); - builder.setContentIntent(inCallPendingIntent); + builder.setContentIntent(createLaunchPendingIntent(false /* isFullScreen */)); // Set the intent as a full screen intent as well if a call is incoming if (notificationType == NOTIFICATION_INCOMING_CALL && !InCallPresenter.getInstance().isShowingInCallUi()) { - configureFullScreenIntent(builder, inCallPendingIntent, call); + configureFullScreenIntent( + builder, createLaunchPendingIntent(true /* isFullScreen */), call); // Set the notification category for incoming calls builder.setCategory(Notification.CATEGORY_CALL); } @@ -339,8 +349,10 @@ public class StatusBarNotifier implements InCallPresenter.InCallStateListener, + mCurrentNotification); mNotificationManager.cancel(mCurrentNotification); } + Log.i(this, "Displaying notification for " + notificationType); mNotificationManager.notify(notificationType, notification); + call.getLatencyReport().onNotificationShown(); mCurrentNotification = notificationType; } @@ -464,6 +476,10 @@ public class StatusBarNotifier implements InCallPresenter.InCallStateListener, 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; } @@ -525,7 +541,11 @@ public class StatusBarNotifier implements InCallPresenter.InCallStateListener, 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; @@ -707,19 +727,24 @@ public class StatusBarNotifier implements InCallPresenter.InCallStateListener, return builder; } - private PendingIntent createLaunchPendingIntent() { - - final Intent intent = InCallPresenter.getInstance().getInCallIntent( + private PendingIntent createLaunchPendingIntent(boolean isFullScreen) { + Intent intent = InCallPresenter.getInstance().getInCallIntent( false /* showDialpad */, false /* newOutgoingCall */); + int requestCode = PENDING_INTENT_REQUEST_CODE_NON_FULL_SCREEN; + if (isFullScreen) { + intent.putExtra(InCallActivity.FOR_FULL_SCREEN_INTENT, true); + // Use a unique request code so that the pending intent isn't clobbered by the + // non-full screen pending intent. + requestCode = PENDING_INTENT_REQUEST_CODE_FULL_SCREEN; + } + // PendingIntent that can be used to launch the InCallActivity. The // system fires off this intent if the user pulls down the windowshade // and clicks the notification's expanded view. It's also used to // launch the InCallActivity immediately when when there's an incoming // call (see the "fullScreenIntent" field below). - PendingIntent inCallPendingIntent = PendingIntent.getActivity(mContext, 0, intent, 0); - - return inCallPendingIntent; + return PendingIntent.getActivity(mContext, requestCode, intent, 0); } /** 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 |