diff options
Diffstat (limited to 'java/com/android/incallui')
10 files changed, 160 insertions, 51 deletions
diff --git a/java/com/android/incallui/AnswerScreenPresenter.java b/java/com/android/incallui/AnswerScreenPresenter.java index 0b79e4be7..e41bac606 100644 --- a/java/com/android/incallui/AnswerScreenPresenter.java +++ b/java/com/android/incallui/AnswerScreenPresenter.java @@ -24,6 +24,7 @@ import android.support.v4.os.UserManagerCompat; import android.telecom.VideoProfile; import com.android.dialer.common.Assert; import com.android.dialer.common.LogUtil; +import com.android.dialer.common.concurrent.DialerExecutorComponent; import com.android.dialer.common.concurrent.ThreadUtil; import com.android.dialer.logging.DialerImpression; import com.android.dialer.logging.Logger; @@ -35,6 +36,9 @@ import com.android.incallui.call.CallList; import com.android.incallui.call.DialerCall; import com.android.incallui.call.DialerCallListener; import com.android.incallui.incalluilock.InCallUiLock; +import com.google.common.util.concurrent.FutureCallback; +import com.google.common.util.concurrent.Futures; +import com.google.common.util.concurrent.ListenableFuture; /** Manages changes for an incoming call screen. */ public class AnswerScreenPresenter @@ -90,6 +94,39 @@ public class AnswerScreenPresenter @Override public void onAnswer(boolean answerVideoAsAudio) { + + DialerCall incomingCall = CallList.getInstance().getIncomingCall(); + InCallActivity inCallActivity = + (InCallActivity) answerScreen.getAnswerScreenFragment().getActivity(); + ListenableFuture<Void> answerPrecondition; + + if (incomingCall != null && inCallActivity != null) { + answerPrecondition = inCallActivity.getSpeakEasyCallManager().onNewIncomingCall(incomingCall); + } else { + answerPrecondition = Futures.immediateFuture(null); + } + + Futures.addCallback( + answerPrecondition, + new FutureCallback<Void>() { + @Override + public void onSuccess(Void result) { + onAnswerCallback(answerVideoAsAudio); + } + + @Override + public void onFailure(Throwable t) { + onAnswerCallback(answerVideoAsAudio); + // TODO(erfanian): Enumerate all error states and specify recovery strategies. + throw new RuntimeException("Failed to successfully complete pre call tasks.", t); + } + }, + DialerExecutorComponent.get(context).uiExecutor()); + addTimeoutCheck(); + } + + private void onAnswerCallback(boolean answerVideoAsAudio) { + if (answerScreen.isVideoUpgradeRequest()) { if (answerVideoAsAudio) { Logger.get(context) @@ -113,7 +150,6 @@ public class AnswerScreenPresenter call.answer(); } } - addTimeoutCheck(); } @Override diff --git a/java/com/android/incallui/NotificationBroadcastReceiver.java b/java/com/android/incallui/NotificationBroadcastReceiver.java index 52d01f5c4..602eb5c5a 100644 --- a/java/com/android/incallui/NotificationBroadcastReceiver.java +++ b/java/com/android/incallui/NotificationBroadcastReceiver.java @@ -20,15 +20,21 @@ import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.os.Build.VERSION_CODES; +import android.support.annotation.NonNull; import android.support.annotation.RequiresApi; import android.telecom.CallAudioState; import android.telecom.VideoProfile; import com.android.dialer.common.LogUtil; +import com.android.dialer.common.concurrent.DialerExecutorComponent; import com.android.dialer.logging.DialerImpression; import com.android.dialer.logging.Logger; import com.android.incallui.call.CallList; import com.android.incallui.call.DialerCall; import com.android.incallui.call.TelecomAdapter; +import com.android.incallui.speakeasy.SpeakEasyCallManager; +import com.google.common.util.concurrent.FutureCallback; +import com.google.common.util.concurrent.Futures; +import com.google.common.util.concurrent.ListenableFuture; /** * Accepts broadcast Intents which will be prepared by {@link StatusBarNotifier} and thus sent from @@ -72,9 +78,9 @@ public class NotificationBroadcastReceiver extends BroadcastReceiver { // TODO: Commands of this nature should exist in the CallList. if (action.equals(ACTION_ANSWER_VIDEO_INCOMING_CALL)) { - answerIncomingCall(VideoProfile.STATE_BIDIRECTIONAL); + answerIncomingCall(VideoProfile.STATE_BIDIRECTIONAL, context); } else if (action.equals(ACTION_ANSWER_VOICE_INCOMING_CALL)) { - answerIncomingCall(VideoProfile.STATE_AUDIO_ONLY); + answerIncomingCall(VideoProfile.STATE_AUDIO_ONLY, context); } else if (action.equals(ACTION_DECLINE_INCOMING_CALL)) { Logger.get(context) .logImpression(DialerImpression.Type.REJECT_INCOMING_CALL_FROM_NOTIFICATION); @@ -140,7 +146,7 @@ public class NotificationBroadcastReceiver extends BroadcastReceiver { } } - private void answerIncomingCall(int videoState) { + private void answerIncomingCall(int videoState, @NonNull Context context) { CallList callList = InCallPresenter.getInstance().getCallList(); if (callList == null) { StatusBarNotifier.clearAllCallNotifications(); @@ -148,13 +154,42 @@ public class NotificationBroadcastReceiver extends BroadcastReceiver { } else { DialerCall call = callList.getIncomingCall(); if (call != null) { - call.answer(videoState); - InCallPresenter.getInstance() - .showInCall(false /* showDialpad */, false /* newOutgoingCall */); + + SpeakEasyCallManager speakEasyCallManager = + InCallPresenter.getInstance().getSpeakEasyCallManager(); + ListenableFuture<Void> answerPrecondition; + + if (speakEasyCallManager != null) { + answerPrecondition = speakEasyCallManager.onNewIncomingCall(call); + } else { + answerPrecondition = Futures.immediateFuture(null); + } + + Futures.addCallback( + answerPrecondition, + new FutureCallback<Void>() { + @Override + public void onSuccess(Void result) { + answerIncomingCallCallback(call, videoState); + } + + @Override + public void onFailure(Throwable t) { + answerIncomingCallCallback(call, videoState); + // TODO(erfanian): Enumerate all error states and specify recovery strategies. + throw new RuntimeException("Failed to successfully complete pre call tasks.", t); + } + }, + DialerExecutorComponent.get(context).uiExecutor()); } } } + private void answerIncomingCallCallback(@NonNull DialerCall call, int videoState) { + call.answer(videoState); + InCallPresenter.getInstance().showInCall(false /* showDialpad */, false /* newOutgoingCall */); + } + private void declineIncomingCall() { CallList callList = InCallPresenter.getInstance().getCallList(); if (callList == null) { diff --git a/java/com/android/incallui/ReturnToCallController.java b/java/com/android/incallui/ReturnToCallController.java index 96bdda1ba..7c4585ca1 100644 --- a/java/com/android/incallui/ReturnToCallController.java +++ b/java/com/android/incallui/ReturnToCallController.java @@ -196,12 +196,13 @@ public class ReturnToCallController implements InCallUiListener, Listener, Audio newInCallState != inCallState && newInCallState == InCallState.OUTGOING && shouldStartInBubbleMode; + boolean bubbleNeverVisible = (bubble == null || !(bubble.isVisible() || bubble.isDismissed())); if (bubble != null && isNewBackgroundCall) { // If new outgoing call is in bubble mode, update bubble info. // We don't update if new call is not in bubble mode even if the existing call is. bubble.setBubbleInfo(generateBubbleInfoForBackgroundCalling()); } - if ((bubble == null || !(bubble.isVisible() || bubble.isDismissed()) || isNewBackgroundCall) + if (((bubbleNeverVisible && newInCallState != InCallState.OUTGOING) || isNewBackgroundCall) && getCall() != null && !InCallPresenter.getInstance().isShowingInCallUi()) { LogUtil.i("ReturnToCallController.onCallListChange", "going to show bubble"); diff --git a/java/com/android/incallui/call/CallList.java b/java/com/android/incallui/call/CallList.java index 5d9db32d9..0e89ac75d 100644 --- a/java/com/android/incallui/call/CallList.java +++ b/java/com/android/incallui/call/CallList.java @@ -32,6 +32,7 @@ import com.android.dialer.blocking.FilteredNumberAsyncQueryHandler; import com.android.dialer.blocking.FilteredNumbersUtil; import com.android.dialer.common.Assert; import com.android.dialer.common.LogUtil; +import com.android.dialer.common.concurrent.DialerExecutorComponent; import com.android.dialer.enrichedcall.EnrichedCallComponent; import com.android.dialer.enrichedcall.EnrichedCallManager; import com.android.dialer.logging.DialerImpression; @@ -41,10 +42,14 @@ import com.android.dialer.metrics.MetricsComponent; import com.android.dialer.shortcuts.ShortcutUsageReporter; import com.android.dialer.spam.Spam; import com.android.dialer.spam.SpamComponent; +import com.android.dialer.spam.status.SpamStatus; import com.android.dialer.telecom.TelecomCallUtil; import com.android.incallui.call.state.DialerCallState; import com.android.incallui.latencyreport.LatencyReport; import com.android.incallui.videotech.utils.SessionModificationState; +import com.google.common.util.concurrent.FutureCallback; +import com.google.common.util.concurrent.Futures; +import com.google.common.util.concurrent.ListenableFuture; import java.util.Collection; import java.util.Collections; import java.util.Iterator; @@ -146,46 +151,53 @@ public class CallList implements DialerCallDelegate { LogUtil.d("CallList.onCallAdded", "callState=" + call.getState()); if (SpamComponent.get(context).spamSettings().isSpamEnabled()) { String number = TelecomCallUtil.getNumber(telecomCall); - SpamComponent.get(context) - .spam() - .checkSpamStatus( - number, - call.getCountryIso(), - new Spam.Listener() { - @Override - public void onComplete(boolean isSpam) { - boolean isIncomingCall = - call.getState() == DialerCallState.INCOMING - || call.getState() == DialerCallState.CALL_WAITING; - if (isSpam) { - if (!isIncomingCall) { - LogUtil.i( - "CallList.onCallAdded", - "marking spam call as not spam because it's not an incoming call"); - isSpam = false; - } else if (isPotentialEmergencyCallback(context, call)) { - LogUtil.i( - "CallList.onCallAdded", - "marking spam call as not spam because an emergency call was made on this" - + " device recently"); - isSpam = false; - } - } - - if (isIncomingCall) { - Logger.get(context) - .logCallImpression( - isSpam - ? DialerImpression.Type.INCOMING_SPAM_CALL - : DialerImpression.Type.INCOMING_NON_SPAM_CALL, - call.getUniqueCallId(), - call.getTimeAddedMs()); - } - call.setSpam(isSpam); - onUpdateCall(call); - notifyGenericListeners(); + ListenableFuture<SpamStatus> futureSpamStatus = + SpamComponent.get(context).spam().checkSpamStatus(number, call.getCountryIso()); + + Futures.addCallback( + futureSpamStatus, + new FutureCallback<SpamStatus>() { + @Override + public void onSuccess(@Nullable SpamStatus result) { + boolean isIncomingCall = + call.getState() == DialerCallState.INCOMING + || call.getState() == DialerCallState.CALL_WAITING; + boolean isSpam = result.isSpam(); + if (isSpam) { + if (!isIncomingCall) { + LogUtil.i( + "CallList.onCallAdded", + "marking spam call as not spam because it's not an incoming call"); + isSpam = false; + } else if (isPotentialEmergencyCallback(context, call)) { + LogUtil.i( + "CallList.onCallAdded", + "marking spam call as not spam because an emergency call was made on this" + + " device recently"); + isSpam = false; } - }); + } + + if (isIncomingCall) { + Logger.get(context) + .logCallImpression( + isSpam + ? DialerImpression.Type.INCOMING_SPAM_CALL + : DialerImpression.Type.INCOMING_NON_SPAM_CALL, + call.getUniqueCallId(), + call.getTimeAddedMs()); + } + call.setSpam(isSpam); + onUpdateCall(call); + notifyGenericListeners(); + } + + @Override + public void onFailure(Throwable t) { + LogUtil.e("CallList.onFailure", "unable to query spam status", t); + } + }, + DialerExecutorComponent.get(context).uiExecutor()); Trace.beginSection("updateUserMarkedSpamStatus"); updateUserMarkedSpamStatus(call, context, number); diff --git a/java/com/android/incallui/contactgrid/ContactGridManager.java b/java/com/android/incallui/contactgrid/ContactGridManager.java index d8b1f5004..493f2d583 100644 --- a/java/com/android/incallui/contactgrid/ContactGridManager.java +++ b/java/com/android/incallui/contactgrid/ContactGridManager.java @@ -23,6 +23,8 @@ import android.os.SystemClock; import android.support.annotation.Nullable; import android.support.v4.view.ViewCompat; import android.telephony.PhoneNumberUtils; +import android.text.BidiFormatter; +import android.text.TextDirectionHeuristics; import android.text.TextUtils; import android.view.View; import android.view.accessibility.AccessibilityEvent; @@ -416,7 +418,9 @@ public class ContactGridManager { // This is used for carriers like Project Fi to show the callback number for emergency calls. deviceNumberTextView.setText( context.getString( - R.string.contact_grid_callback_number, primaryCallState.callbackNumber())); + R.string.contact_grid_callback_number, + BidiFormatter.getInstance() + .unicodeWrap(primaryCallState.callbackNumber(), TextDirectionHeuristics.LTR))); deviceNumberTextView.setVisibility(View.VISIBLE); if (primaryInfo.shouldShowLocation()) { deviceNumberDivider.setVisibility(View.VISIBLE); diff --git a/java/com/android/incallui/spam/SpamNotificationActivity.java b/java/com/android/incallui/spam/SpamNotificationActivity.java index e10dea381..2cf486874 100644 --- a/java/com/android/incallui/spam/SpamNotificationActivity.java +++ b/java/com/android/incallui/spam/SpamNotificationActivity.java @@ -528,7 +528,7 @@ public class SpamNotificationActivity extends FragmentActivity { } private void maybeShowSpamBlockingPromoAndFinish() { - if (!spamBlockingPromoHelper.shouldShowSpamBlockingPromo()) { + if (!spamBlockingPromoHelper.shouldShowAfterCallSpamBlockingPromo()) { finish(); return; } diff --git a/java/com/android/incallui/spam/SpamNotificationService.java b/java/com/android/incallui/spam/SpamNotificationService.java index b418ea23e..82a943da7 100644 --- a/java/com/android/incallui/spam/SpamNotificationService.java +++ b/java/com/android/incallui/spam/SpamNotificationService.java @@ -122,7 +122,7 @@ public class SpamNotificationService extends Service { ReportingLocation.Type.FEEDBACK_PROMPT, contactLookupResultType); new FilteredNumberAsyncQueryHandler(this).blockNumber(null, number, countryIso); - if (spamBlockingPromoHelper.shouldShowSpamBlockingPromo()) { + if (spamBlockingPromoHelper.shouldShowAfterCallSpamBlockingPromo()) { spamBlockingPromoHelper.showSpamBlockingPromoNotification( notificationTag, notificationId, diff --git a/java/com/android/incallui/speakeasy/SpeakEasyCallManager.java b/java/com/android/incallui/speakeasy/SpeakEasyCallManager.java index 8a815d385..b060f64cb 100644 --- a/java/com/android/incallui/speakeasy/SpeakEasyCallManager.java +++ b/java/com/android/incallui/speakeasy/SpeakEasyCallManager.java @@ -21,6 +21,7 @@ import android.support.annotation.NonNull; import android.support.v4.app.Fragment; import com.android.incallui.call.DialerCall; import com.google.common.base.Optional; +import com.google.common.util.concurrent.ListenableFuture; /** Provides operations necessary to SpeakEasy. */ public interface SpeakEasyCallManager { @@ -40,6 +41,13 @@ public interface SpeakEasyCallManager { void onCallRemoved(@NonNull DialerCall call); /** + * Indicates there is a new incoming call that is about to be answered. + * + * @param call The call which is about to become active. + */ + ListenableFuture<Void> onNewIncomingCall(@NonNull DialerCall call); + + /** * Indicates the feature is available. * * @param context The application context. diff --git a/java/com/android/incallui/speakeasy/SpeakEasyCallManagerStub.java b/java/com/android/incallui/speakeasy/SpeakEasyCallManagerStub.java index a0409737b..da5e88aa3 100644 --- a/java/com/android/incallui/speakeasy/SpeakEasyCallManagerStub.java +++ b/java/com/android/incallui/speakeasy/SpeakEasyCallManagerStub.java @@ -22,6 +22,8 @@ import android.support.annotation.Nullable; import android.support.v4.app.Fragment; import com.android.incallui.call.DialerCall; import com.google.common.base.Optional; +import com.google.common.util.concurrent.Futures; +import com.google.common.util.concurrent.ListenableFuture; import javax.inject.Inject; /** Default implementation of SpeakEasyCallManager. */ @@ -41,6 +43,11 @@ public class SpeakEasyCallManagerStub implements SpeakEasyCallManager { @Override public void onCallRemoved(DialerCall call) {} + @Override + public ListenableFuture<Void> onNewIncomingCall(@NonNull DialerCall call) { + return Futures.immediateFuture(null); + } + /** Always returns false. */ @Override public boolean isAvailable(@NonNull Context unused) { diff --git a/java/com/android/incallui/videotech/duo/DuoVideoTech.java b/java/com/android/incallui/videotech/duo/DuoVideoTech.java index fdaed077b..ac74e54df 100644 --- a/java/com/android/incallui/videotech/duo/DuoVideoTech.java +++ b/java/com/android/incallui/videotech/duo/DuoVideoTech.java @@ -23,6 +23,7 @@ import android.telecom.Call; import android.telecom.PhoneAccountHandle; import com.android.dialer.common.Assert; import com.android.dialer.common.LogUtil; +import com.android.dialer.common.concurrent.DefaultFutureCallback; import com.android.dialer.configprovider.ConfigProviderBindings; import com.android.dialer.duo.Duo; import com.android.dialer.duo.DuoListener; @@ -33,6 +34,8 @@ import com.android.incallui.videotech.VideoTech; import com.android.incallui.videotech.utils.SessionModificationState; import com.google.common.base.Optional; import com.google.common.collect.ImmutableList; +import com.google.common.util.concurrent.Futures; +import com.google.common.util.concurrent.MoreExecutors; public class DuoVideoTech implements VideoTech, DuoListener { private final Duo duo; @@ -77,7 +80,10 @@ public class DuoVideoTech implements VideoTech, DuoListener { if (!isRemoteUpgradeAvailabilityQueried) { LogUtil.v("DuoVideoTech.isAvailable", "reachability unknown, starting remote query"); isRemoteUpgradeAvailabilityQueried = true; - duo.updateReachability(context, ImmutableList.of(callingNumber)); + Futures.addCallback( + duo.updateReachability(context, ImmutableList.of(callingNumber)), + new DefaultFutureCallback<>(), + MoreExecutors.directExecutor()); } return false; |