summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTreehugger Robot <treehugger-gerrit@google.com>2018-05-10 01:14:47 +0000
committerGerrit Code Review <noreply-gerritcodereview@google.com>2018-05-10 01:14:47 +0000
commitced93f2d22bffce3b4cfb9c3bfce862b385537dd (patch)
tree2b4715070bcf45b8a8395ee0ae5c7fbb0ef8274c
parentc49c914e53500fc661ec47e365621c9d33e046ca (diff)
parent2f4a0075e9f546514359eda60a24ac9cd49ea80a (diff)
Merge changes Ia54e3421,Id2176e6e,I0311770e,I79f99c34,I8579afff, ...
* changes: Add some annotations that won't influence aosp. Update answer button logic. Hide new after call spam blocking promo behind an additional flag. Allow the TextView for call log primary text to adjust size when recycled. Format callback phone number. Added getLoggingName() to CallLogDataSource and PhoneLookup interfaces. Do not show bubble for outgoing call if it's not a background call. Always fetch status onResume and add logging for voicemail status in OldMainPeer and Place Duo calls with PreCall Creating CallIntent, AutoValue builder, to replace CallIntentBuilder. Set the DisplayNameSource to PHONE in DefaultLookupUriGenerator. Config correct layout boundaries to accommodate long text (call log & bottom sheet) Use info from EmergencyPhoneLookup to render UI for an emergency number. Implement EmergencyPhoneLookup for checking if a number is an emergency number. Add GlobalSpamListStatus and UserSpamListStatus Move SpamStatus classes into subpackage Delete obsolete checkSpamStatus(Listener) API Update callers of checkSpamStatus to use Future based API Fix bug that showing block option for private number. Add a null check for digitsHint. Don't commit fragment transactions if it's not safe. Add ListenableFuture based APIs for checkSpamStatus Pass activity between new call log's adapter/view holder. Replace assert checks with safety checks instead. Add SimpleSpamStatus and use it in FakeSpam and SpamStub Show calls to/from emergency numbers as "Emergency number" in call log & call details
-rw-r--r--java/com/android/dialer/app/calllog/CallLogAdapter.java22
-rw-r--r--java/com/android/dialer/app/calllog/CallLogListItemViewHolder.java64
-rw-r--r--java/com/android/dialer/app/calllog/IntentProvider.java38
-rw-r--r--java/com/android/dialer/calldetails/CallDetailsActivityCommon.java24
-rw-r--r--java/com/android/dialer/calldetails/CallDetailsHeaderViewHolder.java8
-rw-r--r--java/com/android/dialer/callintent/CallIntent.java314
-rw-r--r--java/com/android/dialer/callintent/CallIntentBuilder.java12
-rw-r--r--java/com/android/dialer/calllog/RefreshAnnotatedCallLogWorker.java7
-rw-r--r--java/com/android/dialer/calllog/database/contract/number_attributes.proto5
-rw-r--r--java/com/android/dialer/calllog/datasources/CallLogDataSource.java7
-rw-r--r--java/com/android/dialer/calllog/datasources/phonelookup/PhoneLookupDataSource.java5
-rw-r--r--java/com/android/dialer/calllog/datasources/systemcalllog/SystemCallLogDataSource.java5
-rw-r--r--java/com/android/dialer/calllog/datasources/voicemail/VoicemailDataSource.java5
-rw-r--r--java/com/android/dialer/calllog/ui/NewCallLogAdapter.java24
-rw-r--r--java/com/android/dialer/calllog/ui/NewCallLogFragment.java7
-rw-r--r--java/com/android/dialer/calllog/ui/NewCallLogViewHolder.java8
-rw-r--r--java/com/android/dialer/calllog/ui/menu/Modules.java1
-rw-r--r--java/com/android/dialer/calllog/ui/res/layout/new_call_log_entry.xml91
-rw-r--r--java/com/android/dialer/calllogutils/CallLogEntryText.java32
-rw-r--r--java/com/android/dialer/calllogutils/CallLogRowActions.java7
-rw-r--r--java/com/android/dialer/calllogutils/NumberAttributesConverter.java3
-rw-r--r--java/com/android/dialer/common/concurrent/DialerExecutorComponent.java2
-rw-r--r--java/com/android/dialer/configprovider/ConfigProviderComponent.java2
-rw-r--r--java/com/android/dialer/configprovider/SharedPrefConfigProviderModule.java3
-rw-r--r--java/com/android/dialer/database/CallLogQueryHandler.java5
-rw-r--r--java/com/android/dialer/dialpadview/DialpadFragment.java8
-rw-r--r--java/com/android/dialer/duo/Duo.java18
-rw-r--r--java/com/android/dialer/duo/DuoComponent.java2
-rw-r--r--java/com/android/dialer/duo/PlaceDuoCallNotifier.java45
-rw-r--r--java/com/android/dialer/duo/PlaceDuoCallReceiver.java77
-rw-r--r--java/com/android/dialer/duo/stub/DuoStub.java12
-rw-r--r--java/com/android/dialer/duo/stub/StubDuoModule.java3
-rw-r--r--java/com/android/dialer/enrichedcall/EnrichedCallComponent.java2
-rw-r--r--java/com/android/dialer/enrichedcall/stub/StubEnrichedCallModule.java3
-rw-r--r--java/com/android/dialer/glidephotomanager/impl/DefaultLookupUriGenerator.java2
-rw-r--r--java/com/android/dialer/historyitemactions/DuoCallModule.java56
-rw-r--r--java/com/android/dialer/historyitemactions/HistoryItemActionModulesBuilder.java41
-rw-r--r--java/com/android/dialer/historyitemactions/history_item_action_module_info.proto5
-rw-r--r--java/com/android/dialer/historyitemactions/res/layout/contact_layout.xml4
-rw-r--r--java/com/android/dialer/main/impl/MainActivity.java8
-rw-r--r--java/com/android/dialer/main/impl/MainSearchController.java62
-rw-r--r--java/com/android/dialer/main/impl/OldMainActivityPeer.java31
-rw-r--r--java/com/android/dialer/main/impl/toolbar/MainToolbar.java18
-rw-r--r--java/com/android/dialer/metrics/MetricsComponent.java2
-rw-r--r--java/com/android/dialer/phonelookup/PhoneLookup.java7
-rw-r--r--java/com/android/dialer/phonelookup/PhoneLookupModule.java3
-rw-r--r--java/com/android/dialer/phonelookup/blockednumber/SystemBlockedNumberPhoneLookup.java5
-rw-r--r--java/com/android/dialer/phonelookup/cequint/CequintPhoneLookup.java5
-rw-r--r--java/com/android/dialer/phonelookup/cnap/CnapPhoneLookup.java5
-rw-r--r--java/com/android/dialer/phonelookup/composite/CompositePhoneLookup.java42
-rw-r--r--java/com/android/dialer/phonelookup/consolidator/PhoneLookupInfoConsolidator.java8
-rw-r--r--java/com/android/dialer/phonelookup/cp2/Cp2DefaultDirectoryPhoneLookup.java5
-rw-r--r--java/com/android/dialer/phonelookup/cp2/Cp2ExtendedDirectoryPhoneLookup.java5
-rw-r--r--java/com/android/dialer/phonelookup/emergency/EmergencyPhoneLookup.java122
-rw-r--r--java/com/android/dialer/phonelookup/phone_lookup_info.proto9
-rw-r--r--java/com/android/dialer/phonelookup/spam/SpamPhoneLookup.java7
-rw-r--r--java/com/android/dialer/precall/PreCallCoordinator.java6
-rw-r--r--java/com/android/dialer/precall/impl/DuoAction.java97
-rw-r--r--java/com/android/dialer/precall/impl/PreCallCoordinatorImpl.java20
-rw-r--r--java/com/android/dialer/precall/impl/PreCallModule.java3
-rw-r--r--java/com/android/dialer/searchfragment/list/NewSearchFragment.java9
-rw-r--r--java/com/android/dialer/spam/Spam.java29
-rw-r--r--java/com/android/dialer/spam/SpamComponent.java2
-rw-r--r--java/com/android/dialer/spam/SpamStub.java34
-rw-r--r--java/com/android/dialer/spam/StubSpamModule.java3
-rw-r--r--java/com/android/dialer/spam/promo/SpamBlockingPromoHelper.java9
-rw-r--r--java/com/android/dialer/spam/status/GlobalSpamListStatus.java52
-rw-r--r--java/com/android/dialer/spam/status/SimpleSpamStatus.java36
-rw-r--r--java/com/android/dialer/spam/status/SpamStatus.java (renamed from java/com/android/dialer/spam/SpamStatus.java)2
-rw-r--r--java/com/android/dialer/spam/status/UserSpamListStatus.java58
-rw-r--r--java/com/android/dialer/speeddial/DisambigDialog.java10
-rw-r--r--java/com/android/dialer/speeddial/SpeedDialFragment.java22
-rw-r--r--java/com/android/dialer/storage/StorageComponent.java2
-rw-r--r--java/com/android/dialer/storage/StorageModule.java3
-rw-r--r--java/com/android/dialer/voicemail/listui/menu/Modules.java1
-rw-r--r--java/com/android/incallui/AnswerScreenPresenter.java38
-rw-r--r--java/com/android/incallui/NotificationBroadcastReceiver.java47
-rw-r--r--java/com/android/incallui/ReturnToCallController.java3
-rw-r--r--java/com/android/incallui/call/CallList.java90
-rw-r--r--java/com/android/incallui/contactgrid/ContactGridManager.java6
-rw-r--r--java/com/android/incallui/spam/SpamNotificationActivity.java2
-rw-r--r--java/com/android/incallui/spam/SpamNotificationService.java2
-rw-r--r--java/com/android/incallui/speakeasy/SpeakEasyCallManager.java8
-rw-r--r--java/com/android/incallui/speakeasy/SpeakEasyCallManagerStub.java7
-rw-r--r--java/com/android/incallui/videotech/duo/DuoVideoTech.java8
-rw-r--r--java/com/android/voicemail/VoicemailComponent.java2
-rw-r--r--java/com/android/voicemail/stub/StubVoicemailModule.java3
87 files changed, 1381 insertions, 556 deletions
diff --git a/java/com/android/dialer/app/calllog/CallLogAdapter.java b/java/com/android/dialer/app/calllog/CallLogAdapter.java
index b99cef11f..c2c753e43 100644
--- a/java/com/android/dialer/app/calllog/CallLogAdapter.java
+++ b/java/com/android/dialer/app/calllog/CallLogAdapter.java
@@ -64,7 +64,6 @@ import com.android.dialer.app.voicemail.VoicemailPlaybackPresenter.OnVoicemailDe
import com.android.dialer.blocking.FilteredNumberAsyncQueryHandler;
import com.android.dialer.calldetails.CallDetailsEntries;
import com.android.dialer.calldetails.CallDetailsEntries.CallDetailsEntry;
-import com.android.dialer.callintent.CallIntentBuilder;
import com.android.dialer.calllogutils.CallbackActionHelper.CallbackAction;
import com.android.dialer.calllogutils.PhoneCallDetails;
import com.android.dialer.common.Assert;
@@ -407,28 +406,7 @@ public class CallLogAdapter extends GroupingListAdapter
}
}
expandViewHolderActions(viewHolder);
-
- if (isDuoCallButtonVisible(viewHolder.videoCallButtonView)) {
- CallIntentBuilder.increaseLightbringerCallButtonAppearInExpandedCallLogItemCount();
- }
- }
- }
-
- private boolean isDuoCallButtonVisible(View videoCallButtonView) {
- if (videoCallButtonView == null) {
- return false;
- }
- if (videoCallButtonView.getVisibility() != View.VISIBLE) {
- return false;
- }
- IntentProvider intentProvider = (IntentProvider) videoCallButtonView.getTag();
- if (intentProvider == null) {
- return false;
}
- return DuoComponent.get(activity)
- .getDuo()
- .getIntentType(intentProvider.getIntent(activity))
- .isPresent();
}
};
diff --git a/java/com/android/dialer/app/calllog/CallLogListItemViewHolder.java b/java/com/android/dialer/app/calllog/CallLogListItemViewHolder.java
index 54748387e..9b7741df4 100644
--- a/java/com/android/dialer/app/calllog/CallLogListItemViewHolder.java
+++ b/java/com/android/dialer/app/calllog/CallLogListItemViewHolder.java
@@ -19,7 +19,6 @@ package com.android.dialer.app.calllog;
import android.Manifest.permission;
import android.annotation.SuppressLint;
import android.app.Activity;
-import android.content.ActivityNotFoundException;
import android.content.Context;
import android.content.Intent;
import android.content.res.Resources;
@@ -51,7 +50,6 @@ import android.view.ViewStub;
import android.widget.ImageButton;
import android.widget.ImageView;
import android.widget.TextView;
-import android.widget.Toast;
import com.android.contacts.common.dialog.CallSubjectDialog;
import com.android.dialer.app.DialtactsActivity;
import com.android.dialer.app.R;
@@ -96,7 +94,6 @@ import com.android.dialer.telecom.TelecomUtil;
import com.android.dialer.util.CallUtil;
import com.android.dialer.util.DialerUtils;
import com.android.dialer.util.UriUtils;
-import com.google.common.base.Optional;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.ref.WeakReference;
@@ -550,7 +547,8 @@ public final class CallLogListItemViewHolder extends RecyclerView.ViewHolder
case CallbackAction.DUO:
if (showDuoPrimaryButton()) {
CallIntentBuilder.increaseLightbringerCallButtonAppearInCollapsedCallLogItemCount();
- primaryActionButtonView.setTag(IntentProvider.getDuoVideoIntentProvider(number));
+ primaryActionButtonView.setTag(
+ IntentProvider.getDuoVideoIntentProvider(number, isNonContactEntry(info)));
} else {
primaryActionButtonView.setTag(IntentProvider.getReturnVideoCallIntentProvider(number));
}
@@ -684,14 +682,17 @@ public final class CallLogListItemViewHolder extends RecyclerView.ViewHolder
boolean identifiedSpamCall = isSpamFeatureEnabled && isSpam;
if (duo.isReachable(context, number)) {
- videoCallButtonView.setTag(IntentProvider.getDuoVideoIntentProvider(number));
+ videoCallButtonView.setTag(
+ IntentProvider.getDuoVideoIntentProvider(number, isNonContactEntry(info)));
videoCallButtonView.setVisibility(View.VISIBLE);
+ CallIntentBuilder.increaseLightbringerCallButtonAppearInExpandedCallLogItemCount();
} else if (duo.isActivated(context) && !identifiedSpamCall) {
if (ConfigProviderBindings.get(context)
.getBoolean("enable_call_log_duo_invite_button", false)) {
inviteVideoButtonView.setTag(IntentProvider.getDuoInviteIntentProvider(number));
inviteVideoButtonView.setVisibility(View.VISIBLE);
Logger.get(context).logImpression(DialerImpression.Type.DUO_CALL_LOG_INVITE_SHOWN);
+ CallIntentBuilder.increaseLightbringerCallButtonAppearInExpandedCallLogItemCount();
}
} else if (duo.isEnabled(context) && !identifiedSpamCall) {
if (!duo.isInstalled(context)) {
@@ -701,6 +702,7 @@ public final class CallLogListItemViewHolder extends RecyclerView.ViewHolder
setUpVideoButtonView.setVisibility(View.VISIBLE);
Logger.get(context)
.logImpression(DialerImpression.Type.DUO_CALL_LOG_SET_UP_INSTALL_SHOWN);
+ CallIntentBuilder.increaseLightbringerCallButtonAppearInExpandedCallLogItemCount();
}
} else {
if (ConfigProviderBindings.get(context)
@@ -709,6 +711,7 @@ public final class CallLogListItemViewHolder extends RecyclerView.ViewHolder
setUpVideoButtonView.setVisibility(View.VISIBLE);
Logger.get(context)
.logImpression(DialerImpression.Type.DUO_CALL_LOG_SET_UP_ACTIVATE_SHOWN);
+ CallIntentBuilder.increaseLightbringerCallButtonAppearInExpandedCallLogItemCount();
}
}
}
@@ -782,7 +785,7 @@ public final class CallLogListItemViewHolder extends RecyclerView.ViewHolder
callComposeButtonView.setVisibility(isCallComposerCapable ? View.VISIBLE : View.GONE);
- updateBlockReportActions(isVoicemailNumber);
+ updateBlockReportActions(canPlaceCallToNumber, isVoicemailNumber);
}
private boolean isFullyUndialableVoicemail() {
@@ -1024,20 +1027,13 @@ public final class CallLogListItemViewHolder extends RecyclerView.ViewHolder
if (intentProvider == null) {
return;
}
-
+ intentProvider.logInteraction(context);
final Intent intent = intentProvider.getIntent(context);
// See IntentProvider.getCallDetailIntentProvider() for why this may be null.
if (intent == null) {
return;
}
-
- // We check to see if we are starting a Duo intent. The reason is Duo
- // intents need to be started using startActivityForResult instead of the usual startActivity
- Optional<Duo.IntentType> duoIntentType =
- DuoComponent.get(context).getDuo().getIntentType(intent);
- if (duoIntentType.isPresent()) {
- startDuoActivity(intent, duoIntentType.get());
- } else if (OldCallDetailsActivity.isLaunchIntent(intent)) {
+ if (OldCallDetailsActivity.isLaunchIntent(intent)) {
PerformanceReport.recordClick(UiAction.Type.OPEN_CALL_DETAIL);
((Activity) context)
.startActivityForResult(intent, ActivityRequestCodes.DIALTACTS_CALL_DETAILS);
@@ -1046,9 +1042,6 @@ public final class CallLogListItemViewHolder extends RecyclerView.ViewHolder
&& intent.getIntExtra(TelecomManager.EXTRA_START_CALL_WITH_VIDEO_STATE, -1)
== VideoProfile.STATE_BIDIRECTIONAL) {
Logger.get(context).logImpression(DialerImpression.Type.IMS_VIDEO_REQUESTED_FROM_CALL_LOG);
- } else if (intent.filterEquals(
- DuoComponent.get(context).getDuo().getInstallDuoIntent().orNull())) {
- Logger.get(context).logImpression(DialerImpression.Type.DUO_CALL_LOG_SET_UP_INSTALL);
}
DialerUtils.startActivityWithErrorToast(context, intent);
@@ -1062,32 +1055,6 @@ public final class CallLogListItemViewHolder extends RecyclerView.ViewHolder
return false;
}
- private void startDuoActivity(Intent intent, Duo.IntentType intentType) {
- switch (intentType) {
- case CALL:
- Logger.get(context)
- .logImpression(DialerImpression.Type.LIGHTBRINGER_VIDEO_REQUESTED_FROM_CALL_LOG);
- if (isNonContactEntry(info)) {
- Logger.get(context)
- .logImpression(
- DialerImpression.Type.LIGHTBRINGER_NON_CONTACT_VIDEO_REQUESTED_FROM_CALL_LOG);
- }
- break;
- case INVITE:
- Logger.get(context).logImpression(DialerImpression.Type.DUO_CALL_LOG_INVITE);
- break;
- case ACTIVATE:
- Logger.get(context).logImpression(DialerImpression.Type.DUO_CALL_LOG_SET_UP_ACTIVATE);
- break;
- }
- try {
- Activity activity = (Activity) context;
- activity.startActivityForResult(intent, ActivityRequestCodes.DIALTACTS_DUO);
- } catch (ActivityNotFoundException e) {
- Toast.makeText(context, R.string.activity_not_available, Toast.LENGTH_SHORT).show();
- }
- }
-
private DialerContact buildContact() {
DialerContact.Builder contact = DialerContact.newBuilder();
contact.setPhotoId(info.photoId);
@@ -1172,14 +1139,15 @@ public final class CallLogListItemViewHolder extends RecyclerView.ViewHolder
}
}
- private void updateBlockReportActions(boolean isVoicemailNumber) {
+ private void updateBlockReportActions(boolean canPlaceCallToNumber, boolean isVoicemailNumber) {
// Set block/spam actions.
blockReportView.setVisibility(View.GONE);
blockView.setVisibility(View.GONE);
unblockView.setVisibility(View.GONE);
reportNotSpamView.setVisibility(View.GONE);
String e164Number = PhoneNumberUtils.formatNumberToE164(number, countryIso);
- if (isVoicemailNumber
+ if (!canPlaceCallToNumber
+ || isVoicemailNumber
|| !FilteredNumbersUtil.canBlockNumber(context, e164Number, number)
|| !FilteredNumberCompat.canAttemptBlockOperations(context)) {
return;
@@ -1260,7 +1228,9 @@ public final class CallLogListItemViewHolder extends RecyclerView.ViewHolder
String e164Number = PhoneNumberUtils.formatNumberToE164(number, countryIso);
boolean isVoicemailNumber = callLogCache.isVoicemailNumber(accountHandle, number);
- if (!isVoicemailNumber
+ boolean canPlaceCallToNumber = PhoneNumberHelper.canPlaceCallsTo(number, numberPresentation);
+ if (canPlaceCallToNumber
+ && !isVoicemailNumber
&& FilteredNumbersUtil.canBlockNumber(context, e164Number, number)
&& FilteredNumberCompat.canAttemptBlockOperations(context)) {
boolean isBlocked = blockId != null;
diff --git a/java/com/android/dialer/app/calllog/IntentProvider.java b/java/com/android/dialer/app/calllog/IntentProvider.java
index 1bc726f64..21f341815 100644
--- a/java/com/android/dialer/app/calllog/IntentProvider.java
+++ b/java/com/android/dialer/app/calllog/IntentProvider.java
@@ -32,6 +32,8 @@ import com.android.dialer.callintent.CallInitiationType;
import com.android.dialer.callintent.CallIntentBuilder;
import com.android.dialer.dialercontact.DialerContact;
import com.android.dialer.duo.DuoComponent;
+import com.android.dialer.logging.DialerImpression;
+import com.android.dialer.logging.Logger;
import com.android.dialer.precall.PreCall;
import com.android.dialer.util.IntentUtil;
import java.util.ArrayList;
@@ -93,11 +95,26 @@ public abstract class IntentProvider {
};
}
- public static IntentProvider getDuoVideoIntentProvider(String number) {
+ public static IntentProvider getDuoVideoIntentProvider(String number, boolean isNonContact) {
return new IntentProvider() {
@Override
public Intent getIntent(Context context) {
- return DuoComponent.get(context).getDuo().getCallIntent(number).orNull();
+ return PreCall.getIntent(
+ context,
+ new CallIntentBuilder(number, CallInitiationType.Type.CALL_LOG)
+ .setIsDuoCall(true)
+ .setIsVideoCall(true));
+ }
+
+ @Override
+ public void logInteraction(Context context) {
+ Logger.get(context)
+ .logImpression(DialerImpression.Type.LIGHTBRINGER_VIDEO_REQUESTED_FROM_CALL_LOG);
+ if (isNonContact) {
+ Logger.get(context)
+ .logImpression(
+ DialerImpression.Type.LIGHTBRINGER_NON_CONTACT_VIDEO_REQUESTED_FROM_CALL_LOG);
+ }
}
};
}
@@ -108,6 +125,11 @@ public abstract class IntentProvider {
public Intent getIntent(Context context) {
return DuoComponent.get(context).getDuo().getInstallDuoIntent().orNull();
}
+
+ @Override
+ public void logInteraction(Context context) {
+ Logger.get(context).logImpression(DialerImpression.Type.DUO_CALL_LOG_SET_UP_INSTALL);
+ }
};
}
@@ -117,6 +139,11 @@ public abstract class IntentProvider {
public Intent getIntent(Context context) {
return DuoComponent.get(context).getDuo().getActivateIntent().orNull();
}
+
+ @Override
+ public void logInteraction(Context context) {
+ Logger.get(context).logImpression(DialerImpression.Type.DUO_CALL_LOG_SET_UP_ACTIVATE);
+ }
};
}
@@ -126,6 +153,11 @@ public abstract class IntentProvider {
public Intent getIntent(Context context) {
return DuoComponent.get(context).getDuo().getInviteIntent(number).orNull();
}
+
+ @Override
+ public void logInteraction(Context context) {
+ Logger.get(context).logImpression(DialerImpression.Type.DUO_CALL_LOG_INVITE);
+ }
};
}
@@ -244,4 +276,6 @@ public abstract class IntentProvider {
}
public abstract Intent getIntent(Context context);
+
+ public void logInteraction(Context context) {}
}
diff --git a/java/com/android/dialer/calldetails/CallDetailsActivityCommon.java b/java/com/android/dialer/calldetails/CallDetailsActivityCommon.java
index 80a41919d..86a2676f5 100644
--- a/java/com/android/dialer/calldetails/CallDetailsActivityCommon.java
+++ b/java/com/android/dialer/calldetails/CallDetailsActivityCommon.java
@@ -19,7 +19,6 @@ package com.android.dialer.calldetails;
import android.Manifest.permission;
import android.annotation.SuppressLint;
import android.app.Activity;
-import android.content.ActivityNotFoundException;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
@@ -35,7 +34,6 @@ import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.support.v7.widget.Toolbar;
import android.view.View;
-import android.widget.Toast;
import com.android.dialer.assisteddialing.ui.AssistedDialingSettingActivity;
import com.android.dialer.calldetails.CallDetailsEntries.CallDetailsEntry;
import com.android.dialer.callintent.CallInitiationType;
@@ -48,9 +46,6 @@ import com.android.dialer.common.concurrent.DialerExecutor.Worker;
import com.android.dialer.common.concurrent.DialerExecutorComponent;
import com.android.dialer.common.concurrent.UiListener;
import com.android.dialer.common.database.Selection;
-import com.android.dialer.constants.ActivityRequestCodes;
-import com.android.dialer.duo.Duo;
-import com.android.dialer.duo.DuoComponent;
import com.android.dialer.enrichedcall.EnrichedCallComponent;
import com.android.dialer.enrichedcall.EnrichedCallManager;
import com.android.dialer.enrichedcall.historyquery.proto.HistoryResult;
@@ -63,7 +58,6 @@ import com.android.dialer.postcall.PostCall;
import com.android.dialer.precall.PreCall;
import com.android.dialer.rtt.RttTranscriptActivity;
import com.android.dialer.rtt.RttTranscriptUtil;
-import com.google.common.base.Optional;
import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableSet;
import com.google.i18n.phonenumbers.NumberParseException;
@@ -327,19 +321,11 @@ abstract class CallDetailsActivityCommon extends AppCompatActivity {
public void placeDuoVideoCall(String phoneNumber) {
Logger.get(getActivity())
.logImpression(DialerImpression.Type.CALL_DETAILS_LIGHTBRINGER_CALL_BACK);
- Duo duo = DuoComponent.get(getActivity()).getDuo();
- Optional<Intent> intentOptional = duo.getCallIntent(phoneNumber);
- if (!duo.isReachable(getActivity(), phoneNumber) || !intentOptional.isPresent()) {
- placeImsVideoCall(phoneNumber);
- return;
- }
-
- try {
- getActivity()
- .startActivityForResult(intentOptional.get(), ActivityRequestCodes.DIALTACTS_DUO);
- } catch (ActivityNotFoundException e) {
- Toast.makeText(getActivity(), R.string.activity_not_available, Toast.LENGTH_SHORT).show();
- }
+ PreCall.start(
+ getActivity(),
+ new CallIntentBuilder(phoneNumber, CallInitiationType.Type.CALL_DETAILS)
+ .setIsDuoCall(true)
+ .setIsVideoCall(true));
}
@Override
diff --git a/java/com/android/dialer/calldetails/CallDetailsHeaderViewHolder.java b/java/com/android/dialer/calldetails/CallDetailsHeaderViewHolder.java
index cd1752d74..44b5a4319 100644
--- a/java/com/android/dialer/calldetails/CallDetailsHeaderViewHolder.java
+++ b/java/com/android/dialer/calldetails/CallDetailsHeaderViewHolder.java
@@ -190,7 +190,13 @@ public class CallDetailsHeaderViewHolder extends RecyclerView.ViewHolder
.loadQuickContactBadge(contactPhoto, headerInfo.getPhotoInfo());
nameView.setText(headerInfo.getPrimaryText());
- numberView.setText(headerInfo.getSecondaryText());
+ if (!headerInfo.getSecondaryText().isEmpty()) {
+ numberView.setVisibility(View.VISIBLE);
+ numberView.setText(headerInfo.getSecondaryText());
+ } else {
+ numberView.setVisibility(View.GONE);
+ numberView.setText(null);
+ }
setCallbackAction(callbackAction);
}
diff --git a/java/com/android/dialer/callintent/CallIntent.java b/java/com/android/dialer/callintent/CallIntent.java
new file mode 100644
index 000000000..ba61d5619
--- /dev/null
+++ b/java/com/android/dialer/callintent/CallIntent.java
@@ -0,0 +1,314 @@
+/*
+ * Copyright (C) 2018 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.callintent;
+
+import android.content.Intent;
+import android.net.Uri;
+import android.os.Bundle;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.os.SystemClock;
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
+import android.support.annotation.VisibleForTesting;
+import android.telecom.PhoneAccount;
+import android.telecom.PhoneAccountHandle;
+import android.telecom.TelecomManager;
+import android.telecom.VideoProfile;
+import android.text.TextUtils;
+import com.android.dialer.common.Assert;
+import com.android.dialer.performancereport.PerformanceReport;
+import com.android.dialer.util.CallUtil;
+import com.google.auto.value.AutoValue;
+import com.google.common.collect.ImmutableMap;
+import com.google.protobuf.InvalidProtocolBufferException;
+
+/** Creates an intent to start a new outgoing call. */
+@AutoValue
+public abstract class CallIntent implements Parcelable {
+ private static int lightbringerButtonAppearInExpandedCallLogItemCount = 0;
+ private static int lightbringerButtonAppearInCollapsedCallLogItemCount = 0;
+ private static int lightbringerButtonAppearInSearchCount = 0;
+
+ abstract Uri number();
+
+ abstract CallSpecificAppData callSpecificAppData();
+
+ @Nullable
+ abstract PhoneAccountHandle phoneAccountHandle();
+
+ abstract boolean isVideoCall();
+
+ @Nullable
+ abstract String callSubject();
+
+ abstract boolean allowAssistedDial();
+
+ abstract ImmutableMap<String, String> stringInCallUiIntentExtras();
+
+ abstract ImmutableMap<String, Long> longInCallUiIntentExtras();
+
+ abstract ImmutableMap<String, String> stringPlaceCallExtras();
+
+ abstract ImmutableMap<String, Long> longPlaceCallExtras();
+
+ public static Builder builder() {
+ return new AutoValue_CallIntent.Builder().setIsVideoCall(false).setAllowAssistedDial(false);
+ }
+
+ public abstract Builder toBuilder();
+
+ /** Builder class for CallIntent info. */
+ @AutoValue.Builder
+ public abstract static class Builder {
+ public Builder setTelNumber(String number) {
+ return setNumber(CallUtil.getCallUri(Assert.isNotNull(number)));
+ }
+
+ public Builder setVoicemailNumber(@Nullable PhoneAccountHandle phoneAccountHandle) {
+ return setNumber(Uri.fromParts(PhoneAccount.SCHEME_VOICEMAIL, "", null))
+ .setPhoneAccountHandle(phoneAccountHandle);
+ }
+
+ public abstract Builder setNumber(@NonNull Uri number);
+
+ public Builder setCallInitiationType(CallInitiationType.Type callInitiationType) {
+ return setCallSpecificAppData(
+ CallSpecificAppData.newBuilder().setCallInitiationType(callInitiationType).build());
+ }
+
+ abstract CallSpecificAppData callSpecificAppData();
+
+ public abstract Builder setCallSpecificAppData(
+ @NonNull CallSpecificAppData callSpecificAppData);
+
+ public abstract Builder setPhoneAccountHandle(PhoneAccountHandle phoneAccountHandle);
+
+ public abstract Builder setIsVideoCall(boolean isVideoCall);
+
+ public abstract Builder setCallSubject(String callSubject);
+
+ public abstract Builder setAllowAssistedDial(boolean allowAssistedDial);
+
+ abstract ImmutableMap.Builder<String, String> stringInCallUiIntentExtrasBuilder();
+
+ abstract ImmutableMap.Builder<String, Long> longInCallUiIntentExtrasBuilder();
+
+ public Builder addInCallUiIntentExtra(String key, String value) {
+ stringInCallUiIntentExtrasBuilder().put(key, value);
+ return this;
+ }
+
+ public Builder addInCallUiIntentExtra(String key, Long value) {
+ longInCallUiIntentExtrasBuilder().put(key, value);
+ return this;
+ }
+
+ abstract ImmutableMap.Builder<String, String> stringPlaceCallExtrasBuilder();
+
+ abstract ImmutableMap.Builder<String, Long> longPlaceCallExtrasBuilder();
+
+ public Builder addPlaceCallExtra(String key, String value) {
+ stringPlaceCallExtrasBuilder().put(key, value);
+ return this;
+ }
+
+ public Builder addPlaceCallExtra(String key, Long value) {
+ longPlaceCallExtrasBuilder().put(key, value);
+ return this;
+ }
+
+ abstract CallIntent autoBuild();
+
+ public Intent build() {
+ CallSpecificAppData.Builder builder =
+ CallSpecificAppData.newBuilder(callSpecificAppData())
+ .setLightbringerButtonAppearInExpandedCallLogItemCount(
+ lightbringerButtonAppearInExpandedCallLogItemCount)
+ .setLightbringerButtonAppearInCollapsedCallLogItemCount(
+ lightbringerButtonAppearInCollapsedCallLogItemCount)
+ .setLightbringerButtonAppearInSearchCount(lightbringerButtonAppearInSearchCount);
+ lightbringerButtonAppearInExpandedCallLogItemCount = 0;
+ lightbringerButtonAppearInCollapsedCallLogItemCount = 0;
+ lightbringerButtonAppearInSearchCount = 0;
+
+ if (PerformanceReport.isRecording()) {
+ builder
+ .setTimeSinceAppLaunch(PerformanceReport.getTimeSinceAppLaunch())
+ .setTimeSinceFirstClick(PerformanceReport.getTimeSinceFirstClick())
+ .addAllUiActionsSinceAppLaunch(PerformanceReport.getActions())
+ .addAllUiActionTimestampsSinceAppLaunch(PerformanceReport.getActionTimestamps())
+ .setStartingTabIndex(PerformanceReport.getStartingTabIndex())
+ .build();
+ PerformanceReport.stopRecording();
+ }
+
+ setCallSpecificAppData(builder.build());
+
+ // Validate CallIntent.
+ CallIntent callIntent = autoBuild();
+ Assert.isNotNull(callIntent.number());
+ Assert.isNotNull(callIntent.callSpecificAppData());
+ Assert.checkArgument(
+ callIntent.callSpecificAppData().getCallInitiationType()
+ != CallInitiationType.Type.UNKNOWN_INITIATION);
+
+ return autoBuild().newIntent();
+ }
+ }
+
+ // Creates the intent which can start a call
+ private Intent newIntent() {
+ Intent intent = new Intent(Intent.ACTION_CALL, number());
+
+ intent.putExtra(
+ TelecomManager.EXTRA_START_CALL_WITH_VIDEO_STATE,
+ isVideoCall() ? VideoProfile.STATE_BIDIRECTIONAL : VideoProfile.STATE_AUDIO_ONLY);
+
+ Bundle inCallUiIntentExtras = createInCallUiIntentExtras();
+ inCallUiIntentExtras.putLong(
+ Constants.EXTRA_CALL_CREATED_TIME_MILLIS, SystemClock.elapsedRealtime());
+
+ intent.putExtra(TelecomManager.EXTRA_OUTGOING_CALL_EXTRAS, inCallUiIntentExtras);
+
+ if (phoneAccountHandle() != null) {
+ intent.putExtra(TelecomManager.EXTRA_PHONE_ACCOUNT_HANDLE, phoneAccountHandle());
+ }
+
+ if (!TextUtils.isEmpty(callSubject())) {
+ intent.putExtra(TelecomManager.EXTRA_CALL_SUBJECT, callSubject());
+ }
+
+ intent.putExtras(createPlaceCallExtras());
+
+ return intent;
+ }
+
+ @SuppressWarnings("AndroidApiChecker") // Use of Java 8 APIs.
+ private Bundle createInCallUiIntentExtras() {
+ Bundle bundle = new Bundle();
+ stringInCallUiIntentExtras().forEach(bundle::putString);
+ longInCallUiIntentExtras().forEach(bundle::putLong);
+ CallIntentParser.putCallSpecificAppData(bundle, callSpecificAppData());
+ return bundle;
+ }
+
+ @SuppressWarnings("AndroidApiChecker") // Use of Java 8 APIs.
+ private Bundle createPlaceCallExtras() {
+ Bundle bundle = new Bundle();
+ stringPlaceCallExtras().forEach(bundle::putString);
+ longPlaceCallExtras().forEach(bundle::putLong);
+ CallIntentParser.putCallSpecificAppData(bundle, callSpecificAppData());
+ return bundle;
+ }
+
+ public static void increaseLightbringerCallButtonAppearInExpandedCallLogItemCount() {
+ CallIntent.lightbringerButtonAppearInExpandedCallLogItemCount++;
+ }
+
+ public static void increaseLightbringerCallButtonAppearInCollapsedCallLogItemCount() {
+ CallIntent.lightbringerButtonAppearInCollapsedCallLogItemCount++;
+ }
+
+ public static void increaseLightbringerCallButtonAppearInSearchCount() {
+ CallIntent.lightbringerButtonAppearInSearchCount++;
+ }
+
+ @VisibleForTesting
+ public static int getLightbringerButtonAppearInExpandedCallLogItemCount() {
+ return lightbringerButtonAppearInExpandedCallLogItemCount;
+ }
+
+ @VisibleForTesting
+ public static int getLightbringerButtonAppearInCollapsedCallLogItemCount() {
+ return lightbringerButtonAppearInCollapsedCallLogItemCount;
+ }
+
+ @VisibleForTesting
+ public static int getLightbringerButtonAppearInSearchCount() {
+ return lightbringerButtonAppearInSearchCount;
+ }
+
+ @VisibleForTesting(otherwise = VisibleForTesting.NONE)
+ public static void clearLightbringerCounts() {
+ lightbringerButtonAppearInCollapsedCallLogItemCount = 0;
+ lightbringerButtonAppearInExpandedCallLogItemCount = 0;
+ lightbringerButtonAppearInSearchCount = 0;
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @SuppressWarnings("AndroidApiChecker") // Use of Java 8 APIs.
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeParcelable(number(), flags);
+ dest.writeByteArray(callSpecificAppData().toByteArray());
+ dest.writeParcelable(phoneAccountHandle(), flags);
+ dest.writeInt(isVideoCall() ? 1 : 0);
+ dest.writeString(callSubject());
+ dest.writeInt(allowAssistedDial() ? 1 : 0);
+ Bundle stringInCallUiIntentExtrasBundle = new Bundle();
+ stringInCallUiIntentExtras().forEach(stringInCallUiIntentExtrasBundle::putString);
+ dest.writeBundle(stringInCallUiIntentExtrasBundle);
+ Bundle longInCallUiIntentExtrasBundle = new Bundle();
+ longInCallUiIntentExtras().forEach(longInCallUiIntentExtrasBundle::putLong);
+ dest.writeBundle(longInCallUiIntentExtrasBundle);
+ }
+
+ // @TODO(justinmcclain): Investigate deleting the parcelable logic and instead switching
+ // to using an internal proto for serialization.
+ public static final Creator<CallIntent> CREATOR =
+ new Creator<CallIntent>() {
+ @Override
+ public CallIntent createFromParcel(Parcel source) {
+ CallIntent.Builder callIntentBuilder = builder();
+ ClassLoader classLoader = CallIntent.class.getClassLoader();
+ callIntentBuilder.setNumber(source.readParcelable(classLoader));
+ CallSpecificAppData data;
+ try {
+ data = CallSpecificAppData.parseFrom(source.createByteArray());
+ } catch (InvalidProtocolBufferException e) {
+ data = CallSpecificAppData.getDefaultInstance();
+ }
+ callIntentBuilder
+ .setCallSpecificAppData(data)
+ .setPhoneAccountHandle(source.readParcelable(classLoader))
+ .setIsVideoCall(source.readInt() != 0)
+ .setCallSubject(source.readString())
+ .setAllowAssistedDial(source.readInt() != 0);
+ Bundle stringInCallUiIntentExtrasBundle = source.readBundle(classLoader);
+ for (String key : stringInCallUiIntentExtrasBundle.keySet()) {
+ callIntentBuilder.addInCallUiIntentExtra(
+ key, stringInCallUiIntentExtrasBundle.getString(key));
+ }
+ Bundle longInCallUiIntentExtrasBundle = source.readBundle(classLoader);
+ for (String key : longInCallUiIntentExtrasBundle.keySet()) {
+ callIntentBuilder.addInCallUiIntentExtra(
+ key, longInCallUiIntentExtrasBundle.getLong(key));
+ }
+ return callIntentBuilder.autoBuild();
+ }
+
+ @Override
+ public CallIntent[] newArray(int size) {
+ return new CallIntent[0];
+ }
+ };
+}
diff --git a/java/com/android/dialer/callintent/CallIntentBuilder.java b/java/com/android/dialer/callintent/CallIntentBuilder.java
index 92efd392b..613fdf6a3 100644
--- a/java/com/android/dialer/callintent/CallIntentBuilder.java
+++ b/java/com/android/dialer/callintent/CallIntentBuilder.java
@@ -43,6 +43,7 @@ public class CallIntentBuilder implements Parcelable {
private final CallSpecificAppData callSpecificAppData;
@Nullable private PhoneAccountHandle phoneAccountHandle;
private boolean isVideoCall;
+ private boolean isDuoCall;
private String callSubject;
private boolean allowAssistedDial;
@@ -109,6 +110,7 @@ public class CallIntentBuilder implements Parcelable {
callSpecificAppData = data;
phoneAccountHandle = parcel.readParcelable(classLoader);
isVideoCall = parcel.readInt() != 0;
+ isDuoCall = parcel.readInt() != 0;
callSubject = parcel.readString();
allowAssistedDial = parcel.readInt() != 0;
inCallUiIntentExtras.putAll(parcel.readBundle(classLoader));
@@ -152,6 +154,15 @@ public class CallIntentBuilder implements Parcelable {
return isVideoCall;
}
+ public CallIntentBuilder setIsDuoCall(boolean isDuoCall) {
+ this.isDuoCall = isDuoCall;
+ return this;
+ }
+
+ public boolean isDuoCall() {
+ return isDuoCall;
+ }
+
/** Default false. Should only be set to true if the number has a lookup URI. */
public CallIntentBuilder setAllowAssistedDial(boolean allowAssistedDial) {
this.allowAssistedDial = allowAssistedDial;
@@ -267,6 +278,7 @@ public class CallIntentBuilder implements Parcelable {
dest.writeByteArray(callSpecificAppData.toByteArray());
dest.writeParcelable(phoneAccountHandle, flags);
dest.writeInt(isVideoCall ? 1 : 0);
+ dest.writeInt(isDuoCall ? 1 : 0);
dest.writeString(callSubject);
dest.writeInt(allowAssistedDial ? 1 : 0);
dest.writeBundle(inCallUiIntentExtras);
diff --git a/java/com/android/dialer/calllog/RefreshAnnotatedCallLogWorker.java b/java/com/android/dialer/calllog/RefreshAnnotatedCallLogWorker.java
index 32c278838..fb3700efe 100644
--- a/java/com/android/dialer/calllog/RefreshAnnotatedCallLogWorker.java
+++ b/java/com/android/dialer/calllog/RefreshAnnotatedCallLogWorker.java
@@ -156,8 +156,7 @@ public class RefreshAnnotatedCallLogWorker {
for (CallLogDataSource dataSource : dataSources.getDataSourcesIncludingSystemCallLog()) {
ListenableFuture<Boolean> dataSourceDirty = dataSource.isDirty();
isDirtyFutures.add(dataSourceDirty);
- String eventName =
- String.format(Metrics.IS_DIRTY_TEMPLATE, dataSource.getClass().getSimpleName());
+ String eventName = String.format(Metrics.IS_DIRTY_TEMPLATE, dataSource.getLoggingName());
futureTimer.applyTiming(dataSourceDirty, eventName, LogCatMode.LOG_VALUES);
}
// Simultaneously invokes isDirty on all data sources, returning as soon as one returns true.
@@ -242,7 +241,7 @@ public class RefreshAnnotatedCallLogWorker {
private static String eventNameForFill(CallLogDataSource dataSource, boolean isBuilt) {
return String.format(
!isBuilt ? Metrics.INITIAL_FILL_TEMPLATE : Metrics.FILL_TEMPLATE,
- dataSource.getClass().getSimpleName());
+ dataSource.getLoggingName());
}
private static String eventNameForOverallFill(boolean isBuilt) {
@@ -255,7 +254,7 @@ public class RefreshAnnotatedCallLogWorker {
!isBuilt
? Metrics.INITIAL_ON_SUCCESSFUL_FILL_TEMPLATE
: Metrics.ON_SUCCESSFUL_FILL_TEMPLATE,
- dataSource.getClass().getSimpleName());
+ dataSource.getLoggingName());
}
private static String eventNameForOverallOnSuccessfulFill(boolean isBuilt) {
diff --git a/java/com/android/dialer/calllog/database/contract/number_attributes.proto b/java/com/android/dialer/calllog/database/contract/number_attributes.proto
index f42974d36..2c46d1b12 100644
--- a/java/com/android/dialer/calllog/database/contract/number_attributes.proto
+++ b/java/com/android/dialer/calllog/database/contract/number_attributes.proto
@@ -24,7 +24,7 @@ package com.android.dialer;
import "java/com/android/dialer/logging/contact_source.proto";
// Information related to the phone number of the call.
-// Next ID: 14
+// Next ID: 15
message NumberAttributes {
// The name (which may be a person's name or business name, but not a number)
// formatted exactly as it should appear to the user. If the user's locale or
@@ -74,4 +74,7 @@ message NumberAttributes {
// Description of the number's geolocation (e.g., "Mountain View, CA").
// This string is for display purpose only.
optional string geolocation = 13;
+
+ // Whether the number is an emergency number.
+ optional bool is_emergency_number = 14;
} \ No newline at end of file
diff --git a/java/com/android/dialer/calllog/datasources/CallLogDataSource.java b/java/com/android/dialer/calllog/datasources/CallLogDataSource.java
index f6796c767..75f06d5f6 100644
--- a/java/com/android/dialer/calllog/datasources/CallLogDataSource.java
+++ b/java/com/android/dialer/calllog/datasources/CallLogDataSource.java
@@ -113,4 +113,11 @@ public interface CallLogDataSource {
*/
@MainThread
ListenableFuture<Void> clearData();
+
+ /**
+ * The name of this daa source for logging purposes. This is generally the same as the class name
+ * (but should not use methods from {@link Class} because the class names are generally obfuscated
+ * by Proguard.
+ */
+ String getLoggingName();
}
diff --git a/java/com/android/dialer/calllog/datasources/phonelookup/PhoneLookupDataSource.java b/java/com/android/dialer/calllog/datasources/phonelookup/PhoneLookupDataSource.java
index 0de987308..66d29a7ef 100644
--- a/java/com/android/dialer/calllog/datasources/phonelookup/PhoneLookupDataSource.java
+++ b/java/com/android/dialer/calllog/datasources/phonelookup/PhoneLookupDataSource.java
@@ -320,6 +320,11 @@ public final class PhoneLookupDataSource implements CallLogDataSource {
MoreExecutors.directExecutor());
}
+ @Override
+ public String getLoggingName() {
+ return "PhoneLookupDataSource";
+ }
+
private static ImmutableSet<DialerPhoneNumber>
queryDistinctDialerPhoneNumbersFromAnnotatedCallLog(Context appContext) {
ImmutableSet.Builder<DialerPhoneNumber> numbers = ImmutableSet.builder();
diff --git a/java/com/android/dialer/calllog/datasources/systemcalllog/SystemCallLogDataSource.java b/java/com/android/dialer/calllog/datasources/systemcalllog/SystemCallLogDataSource.java
index b5067daac..a08b50eb8 100644
--- a/java/com/android/dialer/calllog/datasources/systemcalllog/SystemCallLogDataSource.java
+++ b/java/com/android/dialer/calllog/datasources/systemcalllog/SystemCallLogDataSource.java
@@ -148,6 +148,11 @@ public class SystemCallLogDataSource implements CallLogDataSource {
}
@Override
+ public String getLoggingName() {
+ return "SystemCallLogDataSource";
+ }
+
+ @Override
public ListenableFuture<Boolean> isDirty() {
return backgroundExecutorService.submit(this::isDirtyInternal);
}
diff --git a/java/com/android/dialer/calllog/datasources/voicemail/VoicemailDataSource.java b/java/com/android/dialer/calllog/datasources/voicemail/VoicemailDataSource.java
index ab9288a1e..7a230220e 100644
--- a/java/com/android/dialer/calllog/datasources/voicemail/VoicemailDataSource.java
+++ b/java/com/android/dialer/calllog/datasources/voicemail/VoicemailDataSource.java
@@ -121,4 +121,9 @@ public class VoicemailDataSource implements CallLogDataSource {
public ListenableFuture<Void> clearData() {
return Futures.immediateFuture(null);
}
+
+ @Override
+ public String getLoggingName() {
+ return "VoicemailDataSource";
+ }
}
diff --git a/java/com/android/dialer/calllog/ui/NewCallLogAdapter.java b/java/com/android/dialer/calllog/ui/NewCallLogAdapter.java
index f373a7c69..7fd8132aa 100644
--- a/java/com/android/dialer/calllog/ui/NewCallLogAdapter.java
+++ b/java/com/android/dialer/calllog/ui/NewCallLogAdapter.java
@@ -15,6 +15,7 @@
*/
package com.android.dialer.calllog.ui;
+import android.app.Activity;
import android.content.Context;
import android.content.SharedPreferences;
import android.database.Cursor;
@@ -72,7 +73,7 @@ final class NewCallLogAdapter extends RecyclerView.Adapter<ViewHolder> {
}
private final Clock clock;
- private final Context context;
+ private final Activity activity;
private final RealtimeRowProcessor realtimeRowProcessor;
private final PopCounts popCounts = new PopCounts();
private final SharedPreferences sharedPref;
@@ -93,12 +94,12 @@ final class NewCallLogAdapter extends RecyclerView.Adapter<ViewHolder> {
/** Position of the "Older" header. Null when it should not be displayed. */
@Nullable private Integer olderHeaderPosition;
- NewCallLogAdapter(Context context, Cursor cursor, Clock clock) {
- this.context = context;
+ NewCallLogAdapter(Activity activity, Cursor cursor, Clock clock) {
+ this.activity = activity;
this.cursor = cursor;
this.clock = clock;
- this.realtimeRowProcessor = CallLogUiComponent.get(context).realtimeRowProcessor();
- this.sharedPref = StorageComponent.get(context).unencryptedSharedPrefs();
+ this.realtimeRowProcessor = CallLogUiComponent.get(activity).realtimeRowProcessor();
+ this.sharedPref = StorageComponent.get(activity).unencryptedSharedPrefs();
this.onScrollListenerForRecordingDuoDisclosureFirstViewTime =
new OnScrollListenerForRecordingDuoDisclosureFirstViewTime(sharedPref, clock);
@@ -175,8 +176,8 @@ final class NewCallLogAdapter extends RecyclerView.Adapter<ViewHolder> {
// Don't show the Duo disclosure card if
// (1) Duo integration is not enabled on the device, or
// (2) Duo is not activated.
- Duo duo = DuoComponent.get(context).getDuo();
- if (!duo.isEnabled(context) || !duo.isActivated(context)) {
+ Duo duo = DuoComponent.get(activity).getDuo();
+ if (!duo.isEnabled(activity) || !duo.isActivated(activity)) {
return false;
}
@@ -218,7 +219,7 @@ final class NewCallLogAdapter extends RecyclerView.Adapter<ViewHolder> {
switch (viewType) {
case RowType.DUO_DISCLOSURE_CARD:
return new DuoDisclosureCardViewHolder(
- LayoutInflater.from(context)
+ LayoutInflater.from(activity)
.inflate(
R.layout.new_call_log_duo_disclosure_card,
viewGroup,
@@ -227,11 +228,12 @@ final class NewCallLogAdapter extends RecyclerView.Adapter<ViewHolder> {
case RowType.HEADER_YESTERDAY:
case RowType.HEADER_OLDER:
return new HeaderViewHolder(
- LayoutInflater.from(context)
+ LayoutInflater.from(activity)
.inflate(R.layout.new_call_log_header, viewGroup, /* attachToRoot = */ false));
case RowType.CALL_LOG_ENTRY:
return new NewCallLogViewHolder(
- LayoutInflater.from(context)
+ activity,
+ LayoutInflater.from(activity)
.inflate(R.layout.new_call_log_entry, viewGroup, /* attachToRoot = */ false),
clock,
realtimeRowProcessor,
@@ -249,7 +251,7 @@ final class NewCallLogAdapter extends RecyclerView.Adapter<ViewHolder> {
((DuoDisclosureCardViewHolder) viewHolder)
.setDismissListener(
unused -> {
- StorageComponent.get(context)
+ StorageComponent.get(activity)
.unencryptedSharedPrefs()
.edit()
.putBoolean(SHARED_PREF_KEY_DUO_DISCLOSURE_DISMISSED, true)
diff --git a/java/com/android/dialer/calllog/ui/NewCallLogFragment.java b/java/com/android/dialer/calllog/ui/NewCallLogFragment.java
index 0f1c2510a..bc5750770 100644
--- a/java/com/android/dialer/calllog/ui/NewCallLogFragment.java
+++ b/java/com/android/dialer/calllog/ui/NewCallLogFragment.java
@@ -15,6 +15,7 @@
*/
package com.android.dialer.calllog.ui;
+import android.app.Activity;
import android.database.Cursor;
import android.os.Bundle;
import android.support.annotation.Nullable;
@@ -30,6 +31,7 @@ import android.view.View;
import android.view.ViewGroup;
import com.android.dialer.calllog.CallLogComponent;
import com.android.dialer.calllog.RefreshAnnotatedCallLogReceiver;
+import com.android.dialer.common.Assert;
import com.android.dialer.common.LogUtil;
import com.android.dialer.common.concurrent.DefaultFutureCallback;
import com.android.dialer.common.concurrent.ThreadUtil;
@@ -229,8 +231,11 @@ public final class NewCallLogFragment extends Fragment implements LoaderCallback
// TODO(zachh): Handle empty cursor by showing empty view.
if (recyclerView.getAdapter() == null) {
recyclerView.setLayoutManager(new LinearLayoutManager(getContext()));
+ // Note: It's not clear if this callback can be invoked when there's no associated activity,
+ // but if crashes are observed here it may be possible to use getContext() instead.
+ Activity activity = Assert.isNotNull(getActivity());
recyclerView.setAdapter(
- new NewCallLogAdapter(getContext(), newCursor, System::currentTimeMillis));
+ new NewCallLogAdapter(activity, newCursor, System::currentTimeMillis));
} else {
((NewCallLogAdapter) recyclerView.getAdapter()).updateCursor(newCursor);
}
diff --git a/java/com/android/dialer/calllog/ui/NewCallLogViewHolder.java b/java/com/android/dialer/calllog/ui/NewCallLogViewHolder.java
index c02d80ede..5f3cd96c4 100644
--- a/java/com/android/dialer/calllog/ui/NewCallLogViewHolder.java
+++ b/java/com/android/dialer/calllog/ui/NewCallLogViewHolder.java
@@ -72,9 +72,13 @@ final class NewCallLogViewHolder extends RecyclerView.ViewHolder {
private long currentRowId;
NewCallLogViewHolder(
- View view, Clock clock, RealtimeRowProcessor realtimeRowProcessor, PopCounts popCounts) {
+ Activity activity,
+ View view,
+ Clock clock,
+ RealtimeRowProcessor realtimeRowProcessor,
+ PopCounts popCounts) {
super(view);
- this.activity = (Activity) view.getContext();
+ this.activity = activity;
contactPhotoView = view.findViewById(R.id.contact_photo_view);
primaryTextView = view.findViewById(R.id.primary_text);
callCountTextView = view.findViewById(R.id.call_count);
diff --git a/java/com/android/dialer/calllog/ui/menu/Modules.java b/java/com/android/dialer/calllog/ui/menu/Modules.java
index b06e0fb1a..cfeca1059 100644
--- a/java/com/android/dialer/calllog/ui/menu/Modules.java
+++ b/java/com/android/dialer/calllog/ui/menu/Modules.java
@@ -126,6 +126,7 @@ final class Modules {
.setCanSupportAssistedDialing(canSupportAssistedDialing(row))
.setCanSupportCarrierVideoCall(row.getNumberAttributes().getCanSupportCarrierVideoCall())
.setIsBlocked(row.getNumberAttributes().getIsBlocked())
+ .setIsEmergencyNumber(row.getNumberAttributes().getIsEmergencyNumber())
.setIsSpam(row.getNumberAttributes().getIsSpam())
.setIsVoicemailCall(row.getIsVoicemailCall())
.setContactSource(row.getNumberAttributes().getContactSource())
diff --git a/java/com/android/dialer/calllog/ui/res/layout/new_call_log_entry.xml b/java/com/android/dialer/calllog/ui/res/layout/new_call_log_entry.xml
index e3052c097..2fc8e7bb8 100644
--- a/java/com/android/dialer/calllog/ui/res/layout/new_call_log_entry.xml
+++ b/java/com/android/dialer/calllog/ui/res/layout/new_call_log_entry.xml
@@ -17,6 +17,7 @@
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:minHeight="72dp">
@@ -29,22 +30,34 @@
android:layout_marginEnd="10dp"
android:layout_centerVertical="true"/>
- <!-- The frame layout is necessary to avoid clipping the icons and ellipsize the text when the
- content is too wide to fit.
- -->
- <FrameLayout
- android:id="@+id/primary_row"
+ <LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
+ android:layout_marginTop="14dp"
android:layout_toEndOf="@+id/contact_photo_view"
- android:layout_toStartOf="@+id/menu_button">
+ android:layout_toStartOf="@+id/menu_button"
+ android:orientation="vertical">
+ <!-- 1st row: primary info -->
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:layout_marginTop="14dp"
android:orientation="horizontal">
+ <!--
+ Important note:
+
+ The following TextView is the only widget that defines a weight in the containing
+ LinearLayout, of which the purpose is to avoid pushing the widgets after it out of the
+ boundary when the text is too long.
+
+ Generally it is more efficient to assign a width/height of 0dp so that the TextView does
+ not have to measure its own size since it will absorb all the remaining space anyway.
+
+ However, as the TextView is part of an entry in the call log's RecyclerView, we must set
+ layout_width to "wrap_content" so that the TextView can adjust its size when recycled for
+ text of different lengths.
+ -->
<TextView
android:id="@+id/primary_text"
style="@style/PrimaryText"
@@ -54,32 +67,29 @@
android:layout_marginEnd="6dp"
android:ellipsize="end"
android:lineSpacingMultiplier="1.5"
- android:singleLine="true"/>
-
+ android:singleLine="true"
+ tools:ignore="InefficientWeight"/>
<ImageView
android:id="@+id/hd_icon"
android:layout_width="wrap_content"
android:layout_height="18dp"
android:layout_gravity="center_vertical"
- android:src="@drawable/quantum_ic_hd_vd_theme_24"
- />
+ android:src="@drawable/quantum_ic_hd_vd_theme_24"/>
<ImageView
android:id="@+id/wifi_icon"
android:layout_width="wrap_content"
android:layout_height="18dp"
android:layout_gravity="center_vertical"
- android:src="@drawable/quantum_ic_signal_wifi_4_bar_vd_theme_24"
- />
+ android:src="@drawable/quantum_ic_signal_wifi_4_bar_vd_theme_24"/>
<ImageView
android:id="@+id/assisted_dial_icon"
android:layout_width="wrap_content"
android:layout_height="18dp"
android:layout_gravity="center_vertical"
- android:src="@drawable/quantum_ic_language_vd_theme_24"
- />
+ android:src="@drawable/quantum_ic_language_vd_theme_24"/>
<TextView
android:id="@+id/call_count"
@@ -90,43 +100,40 @@
android:lineSpacingMultiplier="1.5"/>
</LinearLayout>
- </FrameLayout>
-
- <LinearLayout
- android:id="@+id/secondary_row"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_below="@+id/primary_row"
- android:layout_toEndOf="@+id/contact_photo_view"
- android:orientation="horizontal">
- <ImageView
- android:id="@+id/call_type_icon"
+ <!-- 2nd row: secondary info -->
+ <LinearLayout
android:layout_width="wrap_content"
- android:layout_height="18dp"
- android:layout_gravity="center_vertical"
- />
+ android:layout_height="wrap_content"
+ android:orientation="horizontal">
+ <ImageView
+ android:id="@+id/call_type_icon"
+ android:layout_width="wrap_content"
+ android:layout_height="18dp"
+ android:layout_gravity="center_vertical"/>
+
+ <TextView
+ android:id="@+id/secondary_text"
+ style="@style/SecondaryText"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:ellipsize="end"
+ android:lineSpacingMultiplier="1.4"
+ android:singleLine="true"/>
+
+ </LinearLayout>
+
+ <!-- 3rd row: phone account info -->
<TextView
- android:id="@+id/secondary_text"
+ android:id="@+id/phone_account"
style="@style/SecondaryText"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:ellipsize="end"
- android:lineSpacingMultiplier="1.4"
android:singleLine="true"/>
- </LinearLayout>
- <TextView
- android:id="@+id/phone_account"
- style="@style/SecondaryText"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_below="@+id/secondary_row"
- android:layout_toEndOf="@+id/contact_photo_view"
- android:ellipsize="end"
- android:singleLine="true"
- android:visibility="visible"/>
+ </LinearLayout>
<ImageView
android:id="@+id/menu_button"
diff --git a/java/com/android/dialer/calllogutils/CallLogEntryText.java b/java/com/android/dialer/calllogutils/CallLogEntryText.java
index 1b7bb06fa..54b1e195a 100644
--- a/java/com/android/dialer/calllogutils/CallLogEntryText.java
+++ b/java/com/android/dialer/calllogutils/CallLogEntryText.java
@@ -42,28 +42,35 @@ public final class CallLogEntryText {
* following the primary text.)
*/
public static CharSequence buildPrimaryText(Context context, CoalescedRow row) {
- // Always prefer the presentation name, like "Restricted".
+ // Calls to emergency services should be shown as "Emergency number".
+ if (row.getNumberAttributes().getIsEmergencyNumber()) {
+ return context.getText(R.string.emergency_number);
+ }
+
+ // Otherwise, follow the following order of preferences.
+ // 1st preference: the presentation name, like "Restricted".
Optional<String> presentationName =
PhoneNumberDisplayUtil.getNameForPresentation(context, row.getNumberPresentation());
if (presentationName.isPresent()) {
return presentationName.get();
}
+ // 2nd preference: the voicemail tag if the call is one made to a voicemail box.
if (row.getIsVoicemailCall() && !TextUtils.isEmpty(row.getVoicemailCallTag())) {
return row.getVoicemailCallTag();
}
- // Otherwise prefer the name.
+ // 3rd preference: the name associated with the number.
if (!TextUtils.isEmpty(row.getNumberAttributes().getName())) {
return row.getNumberAttributes().getName();
}
- // Otherwise prefer the formatted number.
+ // 4th preference: the formatted number.
if (!TextUtils.isEmpty(row.getFormattedNumber())) {
return row.getFormattedNumber();
}
- // If there's no formatted number, just return "Unknown".
+ // Last resort: show "Unknown".
return context.getText(R.string.new_call_log_unknown);
}
@@ -73,6 +80,7 @@ public final class CallLogEntryText {
* <p>Rules:
*
* <ul>
+ * <li>For emergency numbers: Date
* <li>For numbers that are not spam or blocked: $Label(, Duo video|Carrier video)?|$Location •
* Date
* <li>For blocked non-spam numbers: Blocked • $Label(, Duo video|Carrier video)?|$Location •
@@ -100,6 +108,12 @@ public final class CallLogEntryText {
*/
public static CharSequence buildSecondaryTextForEntries(
Context context, Clock clock, CoalescedRow row) {
+ // For emergency numbers, the secondary text should contain only the timestamp.
+ if (row.getNumberAttributes().getIsEmergencyNumber()) {
+ return CallLogDates.newCallLogTimestampLabel(
+ context, clock.currentTimeMillis(), row.getTimestamp());
+ }
+
List<CharSequence> components = new ArrayList<>();
if (row.getNumberAttributes().getIsBlocked()) {
@@ -127,6 +141,8 @@ public final class CallLogEntryText {
public static CharSequence buildSecondaryTextForBottomSheet(Context context, CoalescedRow row) {
/*
* Rules:
+ * For emergency numbers:
+ * Number
* For numbers that are not spam or blocked:
* $Label(, Duo video|Carrier video)?|$Location [• NumberIfNoName]?
* For blocked non-spam numbers:
@@ -149,6 +165,14 @@ public final class CallLogEntryText {
* Mobile • 555-1234
* Brooklyn, NJ
*/
+
+ // For emergency numbers, the secondary text should contain only the number.
+ if (row.getNumberAttributes().getIsEmergencyNumber()) {
+ return !row.getFormattedNumber().isEmpty()
+ ? row.getFormattedNumber()
+ : row.getNumber().getNormalizedNumber();
+ }
+
List<CharSequence> components = new ArrayList<>();
if (row.getNumberAttributes().getIsBlocked()) {
diff --git a/java/com/android/dialer/calllogutils/CallLogRowActions.java b/java/com/android/dialer/calllogutils/CallLogRowActions.java
index d23a15f78..65f3c5fd6 100644
--- a/java/com/android/dialer/calllogutils/CallLogRowActions.java
+++ b/java/com/android/dialer/calllogutils/CallLogRowActions.java
@@ -20,6 +20,7 @@ import android.provider.CallLog.Calls;
import com.android.dialer.callintent.CallInitiationType;
import com.android.dialer.callintent.CallIntentBuilder;
import com.android.dialer.calllog.model.CoalescedRow;
+import com.android.dialer.duo.DuoComponent;
import com.android.dialer.precall.PreCall;
/** Actions which can be performed on a call log row. */
@@ -37,6 +38,10 @@ public final class CallLogRowActions {
activity,
new CallIntentBuilder(
row.getNumber().getNormalizedNumber(), CallInitiationType.Type.CALL_LOG)
- .setIsVideoCall((row.getFeatures() & Calls.FEATURES_VIDEO) == Calls.FEATURES_VIDEO));
+ .setIsVideoCall((row.getFeatures() & Calls.FEATURES_VIDEO) == Calls.FEATURES_VIDEO)
+ .setIsDuoCall(
+ DuoComponent.get(activity)
+ .getDuo()
+ .isDuoAccount(row.getPhoneAccountComponentName())));
}
}
diff --git a/java/com/android/dialer/calllogutils/NumberAttributesConverter.java b/java/com/android/dialer/calllogutils/NumberAttributesConverter.java
index 9f07fdac5..8081c4b3f 100644
--- a/java/com/android/dialer/calllogutils/NumberAttributesConverter.java
+++ b/java/com/android/dialer/calllogutils/NumberAttributesConverter.java
@@ -57,6 +57,7 @@ public final class NumberAttributesConverter {
.setIsCp2InfoIncomplete(phoneLookupInfoConsolidator.isDefaultCp2InfoIncomplete())
.setContactSource(phoneLookupInfoConsolidator.getContactSource())
.setCanSupportCarrierVideoCall(phoneLookupInfoConsolidator.canSupportCarrierVideoCall())
- .setGeolocation(phoneLookupInfoConsolidator.getGeolocation());
+ .setGeolocation(phoneLookupInfoConsolidator.getGeolocation())
+ .setIsEmergencyNumber(phoneLookupInfoConsolidator.isEmergencyNumber());
}
}
diff --git a/java/com/android/dialer/common/concurrent/DialerExecutorComponent.java b/java/com/android/dialer/common/concurrent/DialerExecutorComponent.java
index f4552b203..6a2217416 100644
--- a/java/com/android/dialer/common/concurrent/DialerExecutorComponent.java
+++ b/java/com/android/dialer/common/concurrent/DialerExecutorComponent.java
@@ -23,6 +23,7 @@ import com.android.dialer.common.concurrent.Annotations.LightweightExecutor;
import com.android.dialer.common.concurrent.Annotations.NonUiParallel;
import com.android.dialer.common.concurrent.Annotations.Ui;
import com.android.dialer.inject.HasRootComponent;
+import com.android.dialer.inject.IncludeInDialerRoot;
import com.google.common.util.concurrent.ListeningExecutorService;
import dagger.Subcomponent;
import java.util.concurrent.ExecutorService;
@@ -66,6 +67,7 @@ public abstract class DialerExecutorComponent {
}
/** Used to refer to the root application component. */
+ @IncludeInDialerRoot
public interface HasComponent {
DialerExecutorComponent dialerExecutorComponent();
}
diff --git a/java/com/android/dialer/configprovider/ConfigProviderComponent.java b/java/com/android/dialer/configprovider/ConfigProviderComponent.java
index 10d52e749..e974e30bf 100644
--- a/java/com/android/dialer/configprovider/ConfigProviderComponent.java
+++ b/java/com/android/dialer/configprovider/ConfigProviderComponent.java
@@ -19,6 +19,7 @@ package com.android.dialer.configprovider;
import android.content.Context;
import android.support.annotation.NonNull;
import com.android.dialer.inject.HasRootComponent;
+import com.android.dialer.inject.IncludeInDialerRoot;
import dagger.Subcomponent;
/** Dagger component to provide a {@link ConfigProvider}. */
@@ -36,6 +37,7 @@ public abstract class ConfigProviderComponent {
}
/** Used to refer to the root application component. */
+ @IncludeInDialerRoot
public interface HasComponent {
ConfigProviderComponent configProviderComponent();
}
diff --git a/java/com/android/dialer/configprovider/SharedPrefConfigProviderModule.java b/java/com/android/dialer/configprovider/SharedPrefConfigProviderModule.java
index 4af8bfe17..81bed19bd 100644
--- a/java/com/android/dialer/configprovider/SharedPrefConfigProviderModule.java
+++ b/java/com/android/dialer/configprovider/SharedPrefConfigProviderModule.java
@@ -16,12 +16,15 @@
package com.android.dialer.configprovider;
+import com.android.dialer.inject.DialerVariant;
+import com.android.dialer.inject.InstallIn;
import com.android.dialer.storage.StorageModule;
import dagger.Binds;
import dagger.Module;
import javax.inject.Singleton;
/** Dagger module providing {@link ConfigProvider} based on shared preferences. */
+@InstallIn(variants = {DialerVariant.DIALER_TEST})
@Module(includes = StorageModule.class)
public abstract class SharedPrefConfigProviderModule {
diff --git a/java/com/android/dialer/database/CallLogQueryHandler.java b/java/com/android/dialer/database/CallLogQueryHandler.java
index e974cc48f..a18023c4b 100644
--- a/java/com/android/dialer/database/CallLogQueryHandler.java
+++ b/java/com/android/dialer/database/CallLogQueryHandler.java
@@ -112,6 +112,7 @@ public class CallLogQueryHandler extends NoNullCursorAsyncQueryHandler {
.appendOmtpVoicemailStatusSelectionClause(context, where, selectionArgs);
if (TelecomUtil.hasReadWriteVoicemailPermissions(context)) {
+ LogUtil.i("CallLogQueryHandler.fetchVoicemailStatus", "fetching voicemail status");
startQuery(
QUERY_VOICEMAIL_STATUS_TOKEN,
null,
@@ -120,6 +121,10 @@ public class CallLogQueryHandler extends NoNullCursorAsyncQueryHandler {
where.toString(),
selectionArgs.toArray(new String[selectionArgs.size()]),
null);
+ } else {
+ LogUtil.i(
+ "CallLogQueryHandler.fetchVoicemailStatus",
+ "fetching voicemail status failed due to permissions");
}
}
diff --git a/java/com/android/dialer/dialpadview/DialpadFragment.java b/java/com/android/dialer/dialpadview/DialpadFragment.java
index c45c77689..d48870dfc 100644
--- a/java/com/android/dialer/dialpadview/DialpadFragment.java
+++ b/java/com/android/dialer/dialpadview/DialpadFragment.java
@@ -477,7 +477,13 @@ public class DialpadFragment extends Fragment
return;
}
digits.setContentDescription(null);
- digitsHint.setVisibility(View.GONE);
+
+ // TOOD(77908301): Investigate why this is the case
+ // It's not clear why digitsHint would be null when digits is initialized as the time, so adding
+ // a todo to investigate why.
+ if (digitsHint != null) {
+ digitsHint.setVisibility(View.GONE);
+ }
}
/**
diff --git a/java/com/android/dialer/duo/Duo.java b/java/com/android/dialer/duo/Duo.java
index 06a3db063..85fe9fbc1 100644
--- a/java/com/android/dialer/duo/Duo.java
+++ b/java/com/android/dialer/duo/Duo.java
@@ -27,6 +27,8 @@ import android.telecom.Call;
import android.telecom.PhoneAccountHandle;
import com.google.auto.value.AutoValue;
import com.google.common.base.Optional;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.util.concurrent.ListenableFuture;
import java.util.List;
/** Interface for Duo video call integration. */
@@ -61,7 +63,8 @@ public interface Duo {
/** Starts a task to update the reachability of the parameter numbers asynchronously. */
@MainThread
- void updateReachability(@NonNull Context context, @NonNull List<String> numbers);
+ ListenableFuture<ImmutableMap<String, ReachabilityData>> updateReachability(
+ @NonNull Context context, @NonNull List<String> numbers);
/**
* Clears the current reachability data and starts a task to load the latest reachability data
@@ -95,19 +98,6 @@ public interface Duo {
*/
Optional<Intent> getInviteIntent(String number);
- /** Return value of {@link #getIntentType(Intent)} */
- enum IntentType {
- /** The intent is returned by {@link #getCallIntent(String)} */
- CALL,
- /** The intent is returned by {@link #getActivateIntent()} */
- ACTIVATE,
- /** The intent is returned by {@link #getInviteIntent(String)} */
- INVITE
- }
-
- /** Classifies a Duo intent. Absent if the intent is not a Duo intent. */
- Optional<IntentType> getIntentType(Intent intent);
-
Optional<Intent> getInstallDuoIntent();
/** Requests upgrading the parameter ongoing call to a Duo video call. */
diff --git a/java/com/android/dialer/duo/DuoComponent.java b/java/com/android/dialer/duo/DuoComponent.java
index 031ee9e53..307832aaf 100644
--- a/java/com/android/dialer/duo/DuoComponent.java
+++ b/java/com/android/dialer/duo/DuoComponent.java
@@ -19,6 +19,7 @@ package com.android.dialer.duo;
import android.content.Context;
import android.support.annotation.NonNull;
import com.android.dialer.inject.HasRootComponent;
+import com.android.dialer.inject.IncludeInDialerRoot;
import dagger.Subcomponent;
/**
@@ -35,6 +36,7 @@ public abstract class DuoComponent {
}
/** Used to refer to the root application component. */
+ @IncludeInDialerRoot
public interface HasComponent {
DuoComponent duoComponent();
}
diff --git a/java/com/android/dialer/duo/PlaceDuoCallNotifier.java b/java/com/android/dialer/duo/PlaceDuoCallNotifier.java
deleted file mode 100644
index 8fde981a0..000000000
--- a/java/com/android/dialer/duo/PlaceDuoCallNotifier.java
+++ /dev/null
@@ -1,45 +0,0 @@
-/*
- * Copyright (C) 2018 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.duo;
-
-import android.content.Context;
-import android.content.Intent;
-import android.support.v4.content.LocalBroadcastManager;
-import com.android.dialer.common.LogUtil;
-
-/** Notifies that a Duo video call should be started. */
-public final class PlaceDuoCallNotifier {
-
- private PlaceDuoCallNotifier() {}
-
- /**
- * Broadcasts an intent notifying that a Duo call should be started.
- *
- * <p>See {@link PlaceDuoCallReceiver} for how the intent is handled.
- *
- * @param phoneNumber The number to start a Duo call. It can be of any format.
- */
- public static void notify(Context context, String phoneNumber) {
- LogUtil.enterBlock("PlaceDuoCallNotifier.notify");
-
- Intent intent = new Intent();
- intent.setAction(PlaceDuoCallReceiver.ACTION_START_DUO_CALL);
- intent.putExtra(PlaceDuoCallReceiver.EXTRA_PHONE_NUMBER, phoneNumber);
-
- LocalBroadcastManager.getInstance(context).sendBroadcast(intent);
- }
-}
diff --git a/java/com/android/dialer/duo/PlaceDuoCallReceiver.java b/java/com/android/dialer/duo/PlaceDuoCallReceiver.java
deleted file mode 100644
index f504aef57..000000000
--- a/java/com/android/dialer/duo/PlaceDuoCallReceiver.java
+++ /dev/null
@@ -1,77 +0,0 @@
-/*
- * Copyright (C) 2018 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.duo;
-
-import android.app.Activity;
-import android.content.BroadcastReceiver;
-import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
-import com.android.dialer.common.Assert;
-import com.android.dialer.common.LogUtil;
-import com.android.dialer.constants.ActivityRequestCodes;
-
-/** A {@link BroadcastReceiver} that starts a Duo video call. */
-public final class PlaceDuoCallReceiver extends BroadcastReceiver {
-
- static final String ACTION_START_DUO_CALL = "start_duo_call";
- static final String EXTRA_PHONE_NUMBER = "phone_number";
-
- /**
- * {@link Activity} needed to launch Duo.
- *
- * <p>A Duo call can only be placed via {@link Activity#startActivityForResult(Intent, int)}.
- */
- private final Activity activity;
-
- /** Returns an {@link IntentFilter} containing all actions accepted by this broadcast receiver. */
- public static IntentFilter getIntentFilter() {
- IntentFilter intentFilter = new IntentFilter();
- intentFilter.addAction(ACTION_START_DUO_CALL);
- return intentFilter;
- }
-
- public PlaceDuoCallReceiver(Activity activity) {
- this.activity = activity;
- }
-
- @Override
- public void onReceive(Context context, Intent intent) {
- LogUtil.enterBlock("PlaceDuoCallReceiver.onReceive");
-
- String action = intent.getAction();
-
- switch (Assert.isNotNull(action)) {
- case ACTION_START_DUO_CALL:
- startDuoCall(context, intent);
- break;
- default:
- throw new IllegalStateException("Unsupported action: " + action);
- }
- }
-
- private void startDuoCall(Context context, Intent intent) {
- LogUtil.enterBlock("PlaceDuoCallReceiver.startDuoCall");
-
- Assert.checkArgument(intent.hasExtra(EXTRA_PHONE_NUMBER));
- String phoneNumber = intent.getStringExtra(EXTRA_PHONE_NUMBER);
-
- Duo duo = DuoComponent.get(context).getDuo();
- activity.startActivityForResult(
- duo.getCallIntent(phoneNumber).orNull(), ActivityRequestCodes.DIALTACTS_DUO);
- }
-}
diff --git a/java/com/android/dialer/duo/stub/DuoStub.java b/java/com/android/dialer/duo/stub/DuoStub.java
index b0867148f..2131d16f7 100644
--- a/java/com/android/dialer/duo/stub/DuoStub.java
+++ b/java/com/android/dialer/duo/stub/DuoStub.java
@@ -29,6 +29,9 @@ import com.android.dialer.common.Assert;
import com.android.dialer.duo.Duo;
import com.android.dialer.duo.DuoListener;
import com.google.common.base.Optional;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.util.concurrent.Futures;
+import com.google.common.util.concurrent.ListenableFuture;
import java.util.List;
import javax.inject.Inject;
@@ -72,10 +75,12 @@ public class DuoStub implements Duo {
}
@Override
- public void updateReachability(@NonNull Context context, @NonNull List<String> numbers) {
+ public ListenableFuture<ImmutableMap<String, ReachabilityData>> updateReachability(
+ @NonNull Context context, @NonNull List<String> numbers) {
Assert.isMainThread();
Assert.isNotNull(context);
Assert.isNotNull(numbers);
+ return Futures.immediateFuture(ImmutableMap.of());
}
@Override
@@ -115,11 +120,6 @@ public class DuoStub implements Duo {
}
@Override
- public Optional<IntentType> getIntentType(Intent intent) {
- return Optional.absent();
- }
-
- @Override
public Optional<Intent> getInstallDuoIntent() {
return null;
}
diff --git a/java/com/android/dialer/duo/stub/StubDuoModule.java b/java/com/android/dialer/duo/stub/StubDuoModule.java
index 604406a94..8966602bb 100644
--- a/java/com/android/dialer/duo/stub/StubDuoModule.java
+++ b/java/com/android/dialer/duo/stub/StubDuoModule.java
@@ -17,10 +17,13 @@
package com.android.dialer.duo.stub;
import com.android.dialer.duo.Duo;
+import com.android.dialer.inject.DialerVariant;
+import com.android.dialer.inject.InstallIn;
import dagger.Binds;
import dagger.Module;
import javax.inject.Singleton;
+@InstallIn(variants = {DialerVariant.DIALER_TEST})
@Module
public abstract class StubDuoModule {
diff --git a/java/com/android/dialer/enrichedcall/EnrichedCallComponent.java b/java/com/android/dialer/enrichedcall/EnrichedCallComponent.java
index 2ed2e94dc..46afd848d 100644
--- a/java/com/android/dialer/enrichedcall/EnrichedCallComponent.java
+++ b/java/com/android/dialer/enrichedcall/EnrichedCallComponent.java
@@ -19,6 +19,7 @@ package com.android.dialer.enrichedcall;
import android.content.Context;
import android.support.annotation.NonNull;
import com.android.dialer.inject.HasRootComponent;
+import com.android.dialer.inject.IncludeInDialerRoot;
import dagger.Subcomponent;
/** Subcomponent that can be used to access the enriched call implementation. */
@@ -37,6 +38,7 @@ public abstract class EnrichedCallComponent {
}
/** Used to refer to the root application component. */
+ @IncludeInDialerRoot
public interface HasComponent {
EnrichedCallComponent enrichedCallComponent();
}
diff --git a/java/com/android/dialer/enrichedcall/stub/StubEnrichedCallModule.java b/java/com/android/dialer/enrichedcall/stub/StubEnrichedCallModule.java
index 93e15790a..5a0618d3c 100644
--- a/java/com/android/dialer/enrichedcall/stub/StubEnrichedCallModule.java
+++ b/java/com/android/dialer/enrichedcall/stub/StubEnrichedCallModule.java
@@ -18,12 +18,15 @@ package com.android.dialer.enrichedcall.stub;
import com.android.dialer.enrichedcall.EnrichedCallManager;
import com.android.dialer.enrichedcall.RcsVideoShareFactory;
+import com.android.dialer.inject.DialerVariant;
+import com.android.dialer.inject.InstallIn;
import com.android.incallui.videotech.empty.EmptyVideoTech;
import dagger.Module;
import dagger.Provides;
import javax.inject.Singleton;
/** Module which binds {@link EnrichedCallManagerStub}. */
+@InstallIn(variants = {DialerVariant.DIALER_TEST})
@Module
public class StubEnrichedCallModule {
diff --git a/java/com/android/dialer/glidephotomanager/impl/DefaultLookupUriGenerator.java b/java/com/android/dialer/glidephotomanager/impl/DefaultLookupUriGenerator.java
index 6b90e803c..2c4acd4df 100644
--- a/java/com/android/dialer/glidephotomanager/impl/DefaultLookupUriGenerator.java
+++ b/java/com/android/dialer/glidephotomanager/impl/DefaultLookupUriGenerator.java
@@ -48,7 +48,7 @@ final class DefaultLookupUriGenerator {
try {
lookupJson.put(Contacts.DISPLAY_NAME, photoInfo.getFormattedNumber());
// DISPLAY_NAME_SOURCE required by contacts, otherwise the URI will not be recognized.
- lookupJson.put(Contacts.DISPLAY_NAME_SOURCE, DisplayNameSources.STRUCTURED_NAME);
+ lookupJson.put(Contacts.DISPLAY_NAME_SOURCE, DisplayNameSources.PHONE);
JSONObject contactRows = new JSONObject();
JSONObject phone = new JSONObject();
phone.put(CommonDataKinds.Phone.NUMBER, photoInfo.getFormattedNumber());
diff --git a/java/com/android/dialer/historyitemactions/DuoCallModule.java b/java/com/android/dialer/historyitemactions/DuoCallModule.java
deleted file mode 100644
index e6f31e293..000000000
--- a/java/com/android/dialer/historyitemactions/DuoCallModule.java
+++ /dev/null
@@ -1,56 +0,0 @@
-/*
- * Copyright (C) 2018 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.historyitemactions;
-
-import android.Manifest.permission;
-import android.content.Context;
-import android.support.annotation.RequiresPermission;
-import com.android.dialer.duo.PlaceDuoCallNotifier;
-
-/** {@link HistoryItemActionModule} for making a Duo call. */
-public class DuoCallModule implements HistoryItemActionModule {
-
- private final Context context;
- private final String phoneNumber;
-
- /**
- * Creates a module for making a Duo call.
- *
- * @param phoneNumber The number to start a Duo call. It can be of any format.
- */
- public DuoCallModule(Context context, String phoneNumber) {
- this.context = context;
- this.phoneNumber = phoneNumber;
- }
-
- @Override
- public int getStringId() {
- return R.string.video_call;
- }
-
- @Override
- public int getDrawableId() {
- return R.drawable.quantum_ic_videocam_vd_white_24;
- }
-
- @Override
- @RequiresPermission(permission.READ_PHONE_STATE)
- public boolean onClick() {
- PlaceDuoCallNotifier.notify(context, phoneNumber);
- return true; // Close the bottom sheet.
- }
-}
diff --git a/java/com/android/dialer/historyitemactions/HistoryItemActionModulesBuilder.java b/java/com/android/dialer/historyitemactions/HistoryItemActionModulesBuilder.java
index 9af08be50..e1c6c9666 100644
--- a/java/com/android/dialer/historyitemactions/HistoryItemActionModulesBuilder.java
+++ b/java/com/android/dialer/historyitemactions/HistoryItemActionModulesBuilder.java
@@ -118,6 +118,7 @@ public final class HistoryItemActionModulesBuilder {
* <p>This method is a no-op if
*
* <ul>
+ * <li>the call is one made to/received from an emergency number,
* <li>the call is one made to a voicemail box,
* <li>the number is blocked, or
* <li>the number is marked as spam.
@@ -138,25 +139,24 @@ public final class HistoryItemActionModulesBuilder {
* capability, and Duo is available, add a Duo video call module.
*/
public HistoryItemActionModulesBuilder addModuleForVideoCall() {
- if (moduleInfo.getIsVoicemailCall() || moduleInfo.getIsBlocked() || moduleInfo.getIsSpam()) {
+ if (moduleInfo.getIsEmergencyNumber()
+ || moduleInfo.getIsVoicemailCall()
+ || moduleInfo.getIsBlocked()
+ || moduleInfo.getIsSpam()) {
return this;
}
// Do not set PhoneAccountHandle so that regular PreCall logic will be used. The account used to
// place or receive the call should be ignored for carrier video calls.
// TODO(a bug): figure out the correct video call behavior
- HistoryItemActionModule carrierVideoCallModule =
- IntentModule.newCallModule(
- context,
- new CallIntentBuilder(moduleInfo.getNormalizedNumber(), getCallInitiationType())
- .setAllowAssistedDial(moduleInfo.getCanSupportAssistedDialing())
- .setIsVideoCall(true));
- HistoryItemActionModule duoVideoCallModule =
- new DuoCallModule(context, moduleInfo.getNormalizedNumber());
+ CallIntentBuilder callIntentBuilder =
+ new CallIntentBuilder(moduleInfo.getNormalizedNumber(), getCallInitiationType())
+ .setAllowAssistedDial(moduleInfo.getCanSupportAssistedDialing())
+ .setIsVideoCall(true);
// If the module info is for a video call, add an appropriate video call module.
if ((moduleInfo.getFeatures() & Calls.FEATURES_VIDEO) == Calls.FEATURES_VIDEO) {
- modules.add(isDuoCall() && canPlaceDuoCall() ? duoVideoCallModule : carrierVideoCallModule);
+ modules.add(IntentModule.newCallModule(context, callIntentBuilder.setIsDuoCall(isDuoCall())));
return this;
}
@@ -165,9 +165,9 @@ public final class HistoryItemActionModulesBuilder {
//
// The carrier video call module takes precedence over the Duo module.
if (canPlaceCarrierVideoCall()) {
- modules.add(carrierVideoCallModule);
+ modules.add(IntentModule.newCallModule(context, callIntentBuilder));
} else if (canPlaceDuoCall()) {
- modules.add(duoVideoCallModule);
+ modules.add(IntentModule.newCallModule(context, callIntentBuilder.setIsDuoCall(true)));
}
return this;
}
@@ -178,6 +178,7 @@ public final class HistoryItemActionModulesBuilder {
* <p>The method is a no-op if
*
* <ul>
+ * <li>the call is one made to/received from an emergency number,
* <li>the call is one made to a voicemail box,
* <li>the number is blocked, or
* <li>the number is empty.
@@ -186,7 +187,8 @@ public final class HistoryItemActionModulesBuilder {
public HistoryItemActionModulesBuilder addModuleForSendingTextMessage() {
// TODO(zachh): There are other conditions where this module should not be shown
// (e.g., business numbers).
- if (moduleInfo.getIsVoicemailCall()
+ if (moduleInfo.getIsEmergencyNumber()
+ || moduleInfo.getIsVoicemailCall()
|| moduleInfo.getIsBlocked()
|| TextUtils.isEmpty(moduleInfo.getNormalizedNumber())) {
return this;
@@ -217,6 +219,7 @@ public final class HistoryItemActionModulesBuilder {
* <p>The method is a no-op if
*
* <ul>
+ * <li>the call is one made to/received from an emergency number,
* <li>the call is one made to a voicemail box,
* <li>the number is blocked,
* <li>the number is marked as spam,
@@ -225,7 +228,8 @@ public final class HistoryItemActionModulesBuilder {
* </ul>
*/
public HistoryItemActionModulesBuilder addModuleForAddingToContacts() {
- if (moduleInfo.getIsVoicemailCall()
+ if (moduleInfo.getIsEmergencyNumber()
+ || moduleInfo.getIsVoicemailCall()
|| moduleInfo.getIsBlocked()
|| moduleInfo.getIsSpam()
|| isExistingContact()
@@ -253,7 +257,12 @@ public final class HistoryItemActionModulesBuilder {
/**
* Add modules for blocking/unblocking a number and/or marking it as spam/not spam.
*
- * <p>The method is a no-op if the call is one made to a voicemail box.
+ * <p>The method is a no-op if
+ *
+ * <ul>
+ * <li>the call is one made to/received from an emergency number, or
+ * <li>the call is one made to a voicemail box.
+ * </ul>
*
* <p>If a number is marked as spam, add two modules:
*
@@ -267,7 +276,7 @@ public final class HistoryItemActionModulesBuilder {
* <p>If a number is not blocked or marked as spam, add the "Block/Report spam" module.
*/
public HistoryItemActionModulesBuilder addModuleForBlockedOrSpamNumber() {
- if (moduleInfo.getIsVoicemailCall()) {
+ if (moduleInfo.getIsEmergencyNumber() || moduleInfo.getIsVoicemailCall()) {
return this;
}
diff --git a/java/com/android/dialer/historyitemactions/history_item_action_module_info.proto b/java/com/android/dialer/historyitemactions/history_item_action_module_info.proto
index 99071a7cd..f7022c28c 100644
--- a/java/com/android/dialer/historyitemactions/history_item_action_module_info.proto
+++ b/java/com/android/dialer/historyitemactions/history_item_action_module_info.proto
@@ -10,7 +10,7 @@ package com.android.dialer.historyitemactions;
import "java/com/android/dialer/logging/contact_source.proto";
// Contains information needed to construct items (modules) in a bottom sheet.
-// Next ID: 16
+// Next ID: 17
message HistoryItemActionModuleInfo {
// The dialer-normalized version of a phone number.
// See DialerPhoneNumber.normalized_number.
@@ -66,4 +66,7 @@ message HistoryItemActionModuleInfo {
VOICEMAIL = 2;
}
optional Host host = 15;
+
+ // Whether the number is an emergency number.
+ optional bool is_emergency_number = 16;
}
diff --git a/java/com/android/dialer/historyitemactions/res/layout/contact_layout.xml b/java/com/android/dialer/historyitemactions/res/layout/contact_layout.xml
index 721740f97..0790cf470 100644
--- a/java/com/android/dialer/historyitemactions/res/layout/contact_layout.xml
+++ b/java/com/android/dialer/historyitemactions/res/layout/contact_layout.xml
@@ -20,7 +20,8 @@
android:layout_height="match_parent"
android:paddingTop="12dp"
android:paddingBottom="12dp"
- android:paddingEnd="8dp"
+ android:paddingStart="10dp"
+ android:paddingEnd="10dp"
android:gravity="center_vertical"
android:orientation="horizontal"
android:background="#FFFFFF">
@@ -29,7 +30,6 @@
android:id="@+id/contact_photo_view"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:layout_marginStart="10dp"
android:layout_marginEnd="10dp"
android:minHeight="@dimen/contact_actions_image_size"/>
diff --git a/java/com/android/dialer/main/impl/MainActivity.java b/java/com/android/dialer/main/impl/MainActivity.java
index 3f660f56c..2046b048f 100644
--- a/java/com/android/dialer/main/impl/MainActivity.java
+++ b/java/com/android/dialer/main/impl/MainActivity.java
@@ -24,7 +24,6 @@ import com.android.dialer.blockreportspam.ShowBlockReportSpamDialogReceiver;
import com.android.dialer.calllog.config.CallLogConfigComponent;
import com.android.dialer.common.Assert;
import com.android.dialer.common.LogUtil;
-import com.android.dialer.duo.PlaceDuoCallReceiver;
import com.android.dialer.interactions.PhoneNumberInteraction.DisambigDialogDismissedListener;
import com.android.dialer.interactions.PhoneNumberInteraction.InteractionErrorCode;
import com.android.dialer.interactions.PhoneNumberInteraction.InteractionErrorListener;
@@ -48,9 +47,6 @@ public class MainActivity extends TransactionSafeActivity
*/
private ShowBlockReportSpamDialogReceiver showBlockReportSpamDialogReceiver;
- /** {@link android.content.BroadcastReceiver} that starts a Duo call. */
- private PlaceDuoCallReceiver placeDuoCallReceiver;
-
public static Intent getShowCallLogIntent(Context context) {
return getShowTabIntent(context, TabIndex.CALL_LOG);
}
@@ -83,7 +79,6 @@ public class MainActivity extends TransactionSafeActivity
activePeer.onActivityCreate(savedInstanceState);
showBlockReportSpamDialogReceiver = new ShowBlockReportSpamDialogReceiver(getFragmentManager());
- placeDuoCallReceiver = new PlaceDuoCallReceiver(/* activity = */ this);
}
protected MainActivityPeer getNewPeer() {
@@ -109,8 +104,6 @@ public class MainActivity extends TransactionSafeActivity
LocalBroadcastManager.getInstance(this)
.registerReceiver(
showBlockReportSpamDialogReceiver, ShowBlockReportSpamDialogReceiver.getIntentFilter());
- LocalBroadcastManager.getInstance(this)
- .registerReceiver(placeDuoCallReceiver, PlaceDuoCallReceiver.getIntentFilter());
}
@Override
@@ -125,7 +118,6 @@ public class MainActivity extends TransactionSafeActivity
activePeer.onActivityPause();
LocalBroadcastManager.getInstance(this).unregisterReceiver(showBlockReportSpamDialogReceiver);
- LocalBroadcastManager.getInstance(this).unregisterReceiver(placeDuoCallReceiver);
}
@Override
diff --git a/java/com/android/dialer/main/impl/MainSearchController.java b/java/com/android/dialer/main/impl/MainSearchController.java
index b9a6654b6..72c46cc7a 100644
--- a/java/com/android/dialer/main/impl/MainSearchController.java
+++ b/java/com/android/dialer/main/impl/MainSearchController.java
@@ -35,7 +35,6 @@ import com.android.contacts.common.dialog.ClearFrequentsDialog;
import com.android.dialer.app.calllog.CallLogActivity;
import com.android.dialer.app.settings.DialerSettingsActivity;
import com.android.dialer.callintent.CallInitiationType;
-import com.android.dialer.common.Assert;
import com.android.dialer.common.LogUtil;
import com.android.dialer.constants.ActivityRequestCodes;
import com.android.dialer.dialpadview.DialpadFragment;
@@ -136,7 +135,10 @@ public class MainSearchController implements SearchBarListener {
}
private void showDialpad(boolean animate, boolean fromNewIntent) {
- Assert.checkArgument(!isDialpadVisible());
+ if (isDialpadVisible()) {
+ LogUtil.e("MainSearchController.showDialpad", "Dialpad is already visible.");
+ return;
+ }
Logger.get(activity).logScreenView(ScreenEvent.Type.MAIN_DIALPAD, activity);
@@ -187,14 +189,32 @@ public class MainSearchController implements SearchBarListener {
*/
private void hideDialpad(boolean animate) {
LogUtil.enterBlock("MainSearchController.hideDialpad");
- assertDialpadVisible();
+ DialpadFragment dialpadFragment = getDialpadFragment();
+ if (dialpadFragment == null) {
+ LogUtil.e("MainSearchController.hideDialpad", "Dialpad fragment is null.");
+ return;
+ }
+
+ if (!dialpadFragment.isAdded()) {
+ LogUtil.e("MainSearchController.hideDialpad", "Dialpad fragment is not added.");
+ return;
+ }
+
+ if (dialpadFragment.isHidden()) {
+ LogUtil.e("MainSearchController.hideDialpad", "Dialpad fragment is already hidden.");
+ return;
+ }
+
+ if (!dialpadFragment.isDialpadSlideUp()) {
+ LogUtil.e("MainSearchController.hideDialpad", "Dialpad fragment is already slide down.");
+ return;
+ }
fab.show();
toolbar.slideDown(animate, fragmentContainer);
toolbar.transferQueryFromDialpad(getDialpadFragment().getQuery());
activity.setTitle(R.string.main_activity_label);
- DialpadFragment dialpadFragment = getDialpadFragment();
dialpadFragment.setAnimate(animate);
dialpadFragment.slideDown(
animate,
@@ -295,7 +315,22 @@ public class MainSearchController implements SearchBarListener {
/** Calls {@link #hideDialpad(boolean)}, removes the search fragment and clears the dialpad. */
private void closeSearch(boolean animate) {
LogUtil.enterBlock("MainSearchController.closeSearch");
- assertSearchIsVisible();
+ NewSearchFragment searchFragment = getSearchFragment();
+ if (searchFragment == null) {
+ LogUtil.e("MainSearchController.closeSearch", "Search fragment is null.");
+ return;
+ }
+
+ if (!searchFragment.isAdded()) {
+ LogUtil.e("MainSearchController.closeSearch", "Search fragment isn't added.");
+ return;
+ }
+
+ if (searchFragment.isHidden()) {
+ LogUtil.e("MainSearchController.closeSearch", "Search fragment is already hidden.");
+ return;
+ }
+
if (isDialpadVisible()) {
hideDialpad(animate);
} else if (!fab.isShown()) {
@@ -304,7 +339,7 @@ public class MainSearchController implements SearchBarListener {
showBottomNav();
toolbar.collapse(animate);
toolbarShadow.setVisibility(View.GONE);
- activity.getFragmentManager().beginTransaction().hide(getSearchFragment()).commit();
+ activity.getFragmentManager().beginTransaction().hide(searchFragment).commit();
// Clear the dialpad so the phone number isn't persisted between search sessions.
DialpadFragment dialpadFragment = getDialpadFragment();
@@ -341,26 +376,11 @@ public class MainSearchController implements SearchBarListener {
&& fragment.isDialpadSlideUp();
}
- private void assertDialpadVisible() {
- DialpadFragment fragment = getDialpadFragment();
- Assert.checkArgument(fragment != null, "Dialpad Fragment is null");
- Assert.checkArgument(fragment.isAdded(), "Dialpad Fragment is no added");
- Assert.checkArgument(!fragment.isHidden(), "Dialpad Fragment is hidden");
- Assert.checkArgument(fragment.isDialpadSlideUp(), "Dialpad Fragment is slide down");
- }
-
private boolean isSearchVisible() {
NewSearchFragment fragment = getSearchFragment();
return fragment != null && fragment.isAdded() && !fragment.isHidden();
}
- private void assertSearchIsVisible() {
- NewSearchFragment fragment = getSearchFragment();
- Assert.checkArgument(fragment != null, "Search Fragment is null");
- Assert.checkArgument(fragment.isAdded(), "Search Fragment is not added");
- Assert.checkArgument(!fragment.isHidden(), "Search Fragment is hidden.");
- }
-
/** Returns true if the search UI is visible. */
public boolean isInSearch() {
return isSearchVisible();
diff --git a/java/com/android/dialer/main/impl/OldMainActivityPeer.java b/java/com/android/dialer/main/impl/OldMainActivityPeer.java
index e3d42fa9e..e426ed215 100644
--- a/java/com/android/dialer/main/impl/OldMainActivityPeer.java
+++ b/java/com/android/dialer/main/impl/OldMainActivityPeer.java
@@ -460,6 +460,7 @@ public class OldMainActivityPeer implements MainActivityPeer, FragmentUtilListen
@SuppressLint("MissingPermission")
@Override
public void onActivityResume() {
+ LogUtil.enterBlock("MainBottomNavBarBottomNavTabListener.onActivityResume");
callLogFragmentListener.onActivityResume();
// Start the thread that updates the smart dial database if the activity is recreated with a
// language change.
@@ -866,6 +867,10 @@ public class OldMainActivityPeer implements MainActivityPeer, FragmentUtilListen
new ContentObserver(new Handler()) {
@Override
public void onChange(boolean selfChange) {
+ LogUtil.i(
+ "MainCallLogFragmentListener",
+ "voicemailStatusObserver.onChange selfChange:%b",
+ selfChange);
super.onChange(selfChange);
callLogQueryHandler.fetchVoicemailStatus();
}
@@ -885,9 +890,10 @@ public class OldMainActivityPeer implements MainActivityPeer, FragmentUtilListen
}
private void registerVoicemailStatusContentObserver(Context context) {
-
+ LogUtil.enterBlock("MainCallLogFragmentListener.registerVoicemailStatusContentObserver");
if (PermissionsUtil.hasReadVoicemailPermissions(context)
&& PermissionsUtil.hasAddVoicemailPermissions(context)) {
+ LogUtil.i("MainCallLogFragmentListener.registerVoicemailStatusContentObserver", "register");
context
.getContentResolver()
.registerContentObserver(
@@ -1013,11 +1019,12 @@ public class OldMainActivityPeer implements MainActivityPeer, FragmentUtilListen
}
public void onActivityResume() {
+ LogUtil.enterBlock("MainCallLogFragmentListener.onActivityResume");
activityIsAlive = true;
registerVoicemailStatusContentObserver(context);
- if (!bottomNavTabListener.newVoicemailFragmentActive()) {
- callLogQueryHandler.fetchVoicemailStatus();
- }
+ // TODO(a bug): Don't use callLogQueryHandler
+ callLogQueryHandler.fetchVoicemailStatus();
+
if (!bottomNavTabListener.newCallLogFragmentActive()) {
callLogQueryHandler.fetchMissedCallsUnreadCount();
}
@@ -1234,7 +1241,7 @@ public class OldMainActivityPeer implements MainActivityPeer, FragmentUtilListen
private static final String CONTACTS_TAG = "contacts";
private static final String VOICEMAIL_TAG = "voicemail";
- private final AppCompatActivity activity;
+ private final TransactionSafeActivity activity;
private final FragmentManager fragmentManager;
private final android.support.v4.app.FragmentManager supportFragmentManager;
private final FloatingActionButton fab;
@@ -1242,7 +1249,7 @@ public class OldMainActivityPeer implements MainActivityPeer, FragmentUtilListen
@TabIndex private int selectedTab = -1;
private MainBottomNavBarBottomNavTabListener(
- AppCompatActivity activity,
+ TransactionSafeActivity activity,
FragmentManager fragmentManager,
android.support.v4.app.FragmentManager supportFragmentManager,
FloatingActionButton fab) {
@@ -1300,7 +1307,7 @@ public class OldMainActivityPeer implements MainActivityPeer, FragmentUtilListen
android.support.v4.app.Fragment supportFragment =
supportFragmentManager.findFragmentByTag(CALL_LOG_TAG);
if (supportFragment != null) {
- supportFragmentManager.beginTransaction().remove(supportFragment).commit();
+ supportFragmentManager.beginTransaction().remove(supportFragment).commitAllowingStateLoss();
// If the NewCallLogFragment was showing, immediately show the old call log fragment
// instead.
if (selectedTab == TabIndex.CALL_LOG) {
@@ -1317,7 +1324,7 @@ public class OldMainActivityPeer implements MainActivityPeer, FragmentUtilListen
android.support.v4.app.Fragment supportFragment =
supportFragmentManager.findFragmentByTag(VOICEMAIL_TAG);
if (supportFragment != null) {
- supportFragmentManager.beginTransaction().remove(supportFragment).commit();
+ supportFragmentManager.beginTransaction().remove(supportFragment).commitAllowingStateLoss();
// If the NewVoicemailFragment was showing, immediately show the old voicemail fragment
// instead.
if (selectedTab == TabIndex.VOICEMAIL) {
@@ -1445,7 +1452,9 @@ public class OldMainActivityPeer implements MainActivityPeer, FragmentUtilListen
"MainBottomNavBarBottomNavTabListener.showFragment", "Not added yet: " + fragment);
transaction.add(R.id.fragment_container, fragment, tag);
}
- transaction.commit();
+ if (activity.isSafeToCommitTransactions()) {
+ transaction.commit();
+ }
// Handle support fragments.
// TODO(calderwoodra): Handle other new fragments.
@@ -1471,7 +1480,9 @@ public class OldMainActivityPeer implements MainActivityPeer, FragmentUtilListen
"Not added yet: " + supportFragment);
supportTransaction.add(R.id.fragment_container, supportFragment, tag);
}
- supportTransaction.commit();
+ if (activity.isSafeToCommitTransactions()) {
+ supportTransaction.commit();
+ }
}
private void showSupportFragment(
diff --git a/java/com/android/dialer/main/impl/toolbar/MainToolbar.java b/java/com/android/dialer/main/impl/toolbar/MainToolbar.java
index 851d7a0b4..475383bd3 100644
--- a/java/com/android/dialer/main/impl/toolbar/MainToolbar.java
+++ b/java/com/android/dialer/main/impl/toolbar/MainToolbar.java
@@ -28,6 +28,7 @@ import android.view.animation.AccelerateDecelerateInterpolator;
import android.widget.ImageButton;
import android.widget.PopupMenu;
import com.android.dialer.common.Assert;
+import com.android.dialer.common.LogUtil;
import com.android.dialer.util.ViewUtil;
import com.google.common.base.Optional;
@@ -41,7 +42,6 @@ public final class MainToolbar extends Toolbar implements PopupMenu.OnMenuItemCl
private SearchBarView searchBar;
private SearchBarListener listener;
private MainToolbarMenu overflowMenu;
- private boolean isSlideUp;
private boolean hasGlobalLayoutListener;
public MainToolbar(Context context, AttributeSet attrs) {
@@ -78,7 +78,6 @@ public final class MainToolbar extends Toolbar implements PopupMenu.OnMenuItemCl
return;
}
- Assert.checkArgument(!isSlideUp);
if (getHeight() == 0) {
hasGlobalLayoutListener = true;
ViewUtil.doOnGlobalLayout(
@@ -89,7 +88,12 @@ public final class MainToolbar extends Toolbar implements PopupMenu.OnMenuItemCl
});
return;
}
- isSlideUp = true;
+
+ if (isSlideUp()) {
+ LogUtil.e("MainToolbar.slideDown", "Already slide up.");
+ return;
+ }
+
animate()
.translationY(-getHeight())
.setDuration(animate ? SLIDE_DURATION : 0)
@@ -105,8 +109,10 @@ public final class MainToolbar extends Toolbar implements PopupMenu.OnMenuItemCl
/** Slides the toolbar down and back onto the screen. */
public void slideDown(boolean animate, View container) {
- Assert.checkArgument(isSlideUp);
- isSlideUp = false;
+ if (getTranslationY() == 0) {
+ LogUtil.e("MainToolbar.slideDown", "Already slide down.");
+ return;
+ }
animate()
.translationY(0)
.setDuration(animate ? SLIDE_DURATION : 0)
@@ -131,7 +137,7 @@ public final class MainToolbar extends Toolbar implements PopupMenu.OnMenuItemCl
}
public boolean isSlideUp() {
- return isSlideUp;
+ return getHeight() != 0 && getTranslationY() == -getHeight();
}
public boolean isExpanded() {
diff --git a/java/com/android/dialer/metrics/MetricsComponent.java b/java/com/android/dialer/metrics/MetricsComponent.java
index a3570db10..c0d957ba4 100644
--- a/java/com/android/dialer/metrics/MetricsComponent.java
+++ b/java/com/android/dialer/metrics/MetricsComponent.java
@@ -18,6 +18,7 @@ package com.android.dialer.metrics;
import android.content.Context;
import com.android.dialer.inject.HasRootComponent;
+import com.android.dialer.inject.IncludeInDialerRoot;
import dagger.Subcomponent;
/** Component for metrics. */
@@ -37,6 +38,7 @@ public abstract class MetricsComponent {
}
/** Used to refer to the root application component. */
+ @IncludeInDialerRoot
public interface HasComponent {
MetricsComponent metricsComponent();
}
diff --git a/java/com/android/dialer/phonelookup/PhoneLookup.java b/java/com/android/dialer/phonelookup/PhoneLookup.java
index 0b9cbf6eb..1043ee775 100644
--- a/java/com/android/dialer/phonelookup/PhoneLookup.java
+++ b/java/com/android/dialer/phonelookup/PhoneLookup.java
@@ -135,4 +135,11 @@ public interface PhoneLookup<T> {
* disabled (because for example there was a problem with it).
*/
ListenableFuture<Void> clearData();
+
+ /**
+ * The name of this lookup for logging purposes. This is generally the same as the class name (but
+ * should not use methods from {@link Class} because the class names are generally obfuscated by
+ * Proguard.
+ */
+ String getLoggingName();
}
diff --git a/java/com/android/dialer/phonelookup/PhoneLookupModule.java b/java/com/android/dialer/phonelookup/PhoneLookupModule.java
index c6e91c4a4..16aa8e516 100644
--- a/java/com/android/dialer/phonelookup/PhoneLookupModule.java
+++ b/java/com/android/dialer/phonelookup/PhoneLookupModule.java
@@ -21,6 +21,7 @@ import com.android.dialer.phonelookup.cequint.CequintPhoneLookup;
import com.android.dialer.phonelookup.cnap.CnapPhoneLookup;
import com.android.dialer.phonelookup.cp2.Cp2DefaultDirectoryPhoneLookup;
import com.android.dialer.phonelookup.cp2.Cp2ExtendedDirectoryPhoneLookup;
+import com.android.dialer.phonelookup.emergency.EmergencyPhoneLookup;
import com.android.dialer.phonelookup.spam.SpamPhoneLookup;
import com.google.common.collect.ImmutableList;
import dagger.Module;
@@ -37,6 +38,7 @@ public abstract class PhoneLookupModule {
CnapPhoneLookup cnapPhoneLookup,
Cp2DefaultDirectoryPhoneLookup cp2DefaultDirectoryPhoneLookup,
Cp2ExtendedDirectoryPhoneLookup cp2ExtendedDirectoryPhoneLookup,
+ EmergencyPhoneLookup emergencyPhoneLookup,
SystemBlockedNumberPhoneLookup systemBlockedNumberPhoneLookup,
SpamPhoneLookup spamPhoneLookup) {
return ImmutableList.of(
@@ -44,6 +46,7 @@ public abstract class PhoneLookupModule {
cnapPhoneLookup,
cp2DefaultDirectoryPhoneLookup,
cp2ExtendedDirectoryPhoneLookup,
+ emergencyPhoneLookup,
systemBlockedNumberPhoneLookup,
spamPhoneLookup);
}
diff --git a/java/com/android/dialer/phonelookup/blockednumber/SystemBlockedNumberPhoneLookup.java b/java/com/android/dialer/phonelookup/blockednumber/SystemBlockedNumberPhoneLookup.java
index fe6642eef..4192e7932 100644
--- a/java/com/android/dialer/phonelookup/blockednumber/SystemBlockedNumberPhoneLookup.java
+++ b/java/com/android/dialer/phonelookup/blockednumber/SystemBlockedNumberPhoneLookup.java
@@ -181,4 +181,9 @@ public class SystemBlockedNumberPhoneLookup implements PhoneLookup<SystemBlocked
public ListenableFuture<Void> clearData() {
return Futures.immediateFuture(null);
}
+
+ @Override
+ public String getLoggingName() {
+ return "SystemBlockedNumberPhoneLookup";
+ }
}
diff --git a/java/com/android/dialer/phonelookup/cequint/CequintPhoneLookup.java b/java/com/android/dialer/phonelookup/cequint/CequintPhoneLookup.java
index 36d0be40f..b045d0396 100644
--- a/java/com/android/dialer/phonelookup/cequint/CequintPhoneLookup.java
+++ b/java/com/android/dialer/phonelookup/cequint/CequintPhoneLookup.java
@@ -141,6 +141,11 @@ public class CequintPhoneLookup implements PhoneLookup<CequintInfo> {
return Futures.immediateFuture(null);
}
+ @Override
+ public String getLoggingName() {
+ return "CequintPhoneLookup";
+ }
+
/**
* Builds a {@link CequintInfo} proto based on the given {@link CequintCallerIdContact} returned
* by {@link CequintCallerIdManager}.
diff --git a/java/com/android/dialer/phonelookup/cnap/CnapPhoneLookup.java b/java/com/android/dialer/phonelookup/cnap/CnapPhoneLookup.java
index db7c210fc..1b78a8e94 100644
--- a/java/com/android/dialer/phonelookup/cnap/CnapPhoneLookup.java
+++ b/java/com/android/dialer/phonelookup/cnap/CnapPhoneLookup.java
@@ -157,4 +157,9 @@ public final class CnapPhoneLookup implements PhoneLookup<CnapInfo> {
public ListenableFuture<Void> clearData() {
return Futures.immediateFuture(null);
}
+
+ @Override
+ public String getLoggingName() {
+ return "CnapPhoneLookup";
+ }
}
diff --git a/java/com/android/dialer/phonelookup/composite/CompositePhoneLookup.java b/java/com/android/dialer/phonelookup/composite/CompositePhoneLookup.java
index 1ac13df33..83223295b 100644
--- a/java/com/android/dialer/phonelookup/composite/CompositePhoneLookup.java
+++ b/java/com/android/dialer/phonelookup/composite/CompositePhoneLookup.java
@@ -89,13 +89,12 @@ public final class CompositePhoneLookup {
for (PhoneLookup<?> phoneLookup : phoneLookups) {
ListenableFuture<?> lookupFuture = phoneLookup.lookup(appContext, call);
String eventName =
- String.format(Metrics.LOOKUP_FOR_CALL_TEMPLATE, phoneLookup.getClass().getSimpleName());
+ String.format(Metrics.LOOKUP_FOR_CALL_TEMPLATE, phoneLookup.getLoggingName());
futureTimer.applyTiming(lookupFuture, eventName);
futures.add(lookupFuture);
}
ListenableFuture<PhoneLookupInfo> combinedFuture = combineSubMessageFutures(futures);
- String eventName =
- String.format(Metrics.LOOKUP_FOR_CALL_TEMPLATE, CompositePhoneLookup.class.getSimpleName());
+ String eventName = String.format(Metrics.LOOKUP_FOR_CALL_TEMPLATE, getLoggingName());
futureTimer.applyTiming(combinedFuture, eventName);
return combinedFuture;
}
@@ -114,14 +113,12 @@ public final class CompositePhoneLookup {
for (PhoneLookup<?> phoneLookup : phoneLookups) {
ListenableFuture<?> lookupFuture = phoneLookup.lookup(dialerPhoneNumber);
String eventName =
- String.format(Metrics.LOOKUP_FOR_NUMBER_TEMPLATE, phoneLookup.getClass().getSimpleName());
+ String.format(Metrics.LOOKUP_FOR_NUMBER_TEMPLATE, phoneLookup.getLoggingName());
futureTimer.applyTiming(lookupFuture, eventName);
futures.add(lookupFuture);
}
ListenableFuture<PhoneLookupInfo> combinedFuture = combineSubMessageFutures(futures);
- String eventName =
- String.format(
- Metrics.LOOKUP_FOR_NUMBER_TEMPLATE, CompositePhoneLookup.class.getSimpleName());
+ String eventName = String.format(Metrics.LOOKUP_FOR_NUMBER_TEMPLATE, getLoggingName());
futureTimer.applyTiming(combinedFuture, eventName);
return combinedFuture;
}
@@ -133,6 +130,7 @@ public final class CompositePhoneLookup {
return Futures.transform(
Futures.allAsList(subMessageFutures),
subMessages -> {
+ Preconditions.checkNotNull(subMessages);
Builder mergedInfo = PhoneLookupInfo.newBuilder();
for (int i = 0; i < subMessages.size(); i++) {
PhoneLookup phoneLookup = phoneLookups.get(i);
@@ -152,16 +150,14 @@ public final class CompositePhoneLookup {
for (PhoneLookup<?> phoneLookup : phoneLookups) {
ListenableFuture<Boolean> isDirtyFuture = phoneLookup.isDirty(phoneNumbers);
futures.add(isDirtyFuture);
- String eventName =
- String.format(Metrics.IS_DIRTY_TEMPLATE, phoneLookup.getClass().getSimpleName());
+ String eventName = String.format(Metrics.IS_DIRTY_TEMPLATE, phoneLookup.getLoggingName());
futureTimer.applyTiming(isDirtyFuture, eventName, LogCatMode.LOG_VALUES);
}
// Executes all child lookups (possibly in parallel), completing when the first composite lookup
// which returns "true" completes, and cancels the others.
ListenableFuture<Boolean> firstMatching =
DialerFutures.firstMatching(futures, Preconditions::checkNotNull, false /* defaultValue */);
- String eventName =
- String.format(Metrics.IS_DIRTY_TEMPLATE, CompositePhoneLookup.class.getSimpleName());
+ String eventName = String.format(Metrics.IS_DIRTY_TEMPLATE, getLoggingName());
futureTimer.applyTiming(firstMatching, eventName, LogCatMode.LOG_VALUES);
return firstMatching;
}
@@ -178,6 +174,7 @@ public final class CompositePhoneLookup {
return Futures.transformAsync(
callLogState.isBuilt(),
isBuilt -> {
+ Preconditions.checkNotNull(isBuilt);
List<ListenableFuture<ImmutableMap<DialerPhoneNumber, ?>>> futures = new ArrayList<>();
for (PhoneLookup phoneLookup : phoneLookups) {
futures.add(buildSubmapAndGetMostRecentInfo(existingInfoMap, phoneLookup, isBuilt));
@@ -186,6 +183,7 @@ public final class CompositePhoneLookup {
Futures.transform(
Futures.allAsList(futures),
(allMaps) -> {
+ Preconditions.checkNotNull(allMaps);
ImmutableMap.Builder<DialerPhoneNumber, PhoneLookupInfo> combinedMap =
ImmutableMap.builder();
for (DialerPhoneNumber dialerPhoneNumber : existingInfoMap.keySet()) {
@@ -206,7 +204,7 @@ public final class CompositePhoneLookup {
return combinedMap.build();
},
lightweightExecutorService);
- String eventName = getMostRecentInfoEventName(this, isBuilt);
+ String eventName = getMostRecentInfoEventName(getLoggingName(), isBuilt);
futureTimer.applyTiming(combinedFuture, eventName);
return combinedFuture;
},
@@ -224,7 +222,7 @@ public final class CompositePhoneLookup {
phoneLookup.getSubMessage(existingInfoMap.get(dialerPhoneNumber)));
ListenableFuture<ImmutableMap<DialerPhoneNumber, T>> mostRecentInfoFuture =
phoneLookup.getMostRecentInfo(ImmutableMap.copyOf(submap));
- String eventName = getMostRecentInfoEventName(phoneLookup, isBuilt);
+ String eventName = getMostRecentInfoEventName(phoneLookup.getLoggingName(), isBuilt);
futureTimer.applyTiming(mostRecentInfoFuture, eventName);
return mostRecentInfoFuture;
}
@@ -234,17 +232,19 @@ public final class CompositePhoneLookup {
return Futures.transformAsync(
callLogState.isBuilt(),
isBuilt -> {
+ Preconditions.checkNotNull(isBuilt);
List<ListenableFuture<Void>> futures = new ArrayList<>();
for (PhoneLookup<?> phoneLookup : phoneLookups) {
ListenableFuture<Void> phoneLookupFuture = phoneLookup.onSuccessfulBulkUpdate();
futures.add(phoneLookupFuture);
- String eventName = onSuccessfulBulkUpdatedEventName(phoneLookup, isBuilt);
+ String eventName =
+ onSuccessfulBulkUpdatedEventName(phoneLookup.getLoggingName(), isBuilt);
futureTimer.applyTiming(phoneLookupFuture, eventName);
}
ListenableFuture<Void> combinedFuture =
Futures.transform(
Futures.allAsList(futures), unused -> null, lightweightExecutorService);
- String eventName = onSuccessfulBulkUpdatedEventName(this, isBuilt);
+ String eventName = onSuccessfulBulkUpdatedEventName(getLoggingName(), isBuilt);
futureTimer.applyTiming(combinedFuture, eventName);
return combinedFuture;
},
@@ -278,19 +278,23 @@ public final class CompositePhoneLookup {
Futures.allAsList(futures), unused -> null, lightweightExecutorService);
}
- private static String getMostRecentInfoEventName(Object classNameSource, boolean isBuilt) {
+ private static String getMostRecentInfoEventName(String loggingName, boolean isBuilt) {
return String.format(
!isBuilt
? Metrics.INITIAL_GET_MOST_RECENT_INFO_TEMPLATE
: Metrics.GET_MOST_RECENT_INFO_TEMPLATE,
- classNameSource.getClass().getSimpleName());
+ loggingName);
}
- private static String onSuccessfulBulkUpdatedEventName(Object classNameSource, boolean isBuilt) {
+ private static String onSuccessfulBulkUpdatedEventName(String loggingName, boolean isBuilt) {
return String.format(
!isBuilt
? Metrics.INITIAL_ON_SUCCESSFUL_BULK_UPDATE_TEMPLATE
: Metrics.ON_SUCCESSFUL_BULK_UPDATE_TEMPLATE,
- classNameSource.getClass().getSimpleName());
+ loggingName);
+ }
+
+ private String getLoggingName() {
+ return "CompositePhoneLookup";
}
}
diff --git a/java/com/android/dialer/phonelookup/consolidator/PhoneLookupInfoConsolidator.java b/java/com/android/dialer/phonelookup/consolidator/PhoneLookupInfoConsolidator.java
index 23ecc8301..29a0de56b 100644
--- a/java/com/android/dialer/phonelookup/consolidator/PhoneLookupInfoConsolidator.java
+++ b/java/com/android/dialer/phonelookup/consolidator/PhoneLookupInfoConsolidator.java
@@ -347,6 +347,14 @@ public final class PhoneLookupInfoConsolidator {
/**
* The {@link PhoneLookupInfo} passed to the constructor is associated with a number. This method
+ * returns whether the number is an emergency number (e.g., 911 in the U.S.).
+ */
+ public boolean isEmergencyNumber() {
+ return phoneLookupInfo.getEmergencyInfo().getIsEmergencyNumber();
+ }
+
+ /**
+ * The {@link PhoneLookupInfo} passed to the constructor is associated with a number. This method
* returns whether the number can be reported as invalid.
*
* <p>As we currently report invalid numbers via the People API, only numbers from the People API
diff --git a/java/com/android/dialer/phonelookup/cp2/Cp2DefaultDirectoryPhoneLookup.java b/java/com/android/dialer/phonelookup/cp2/Cp2DefaultDirectoryPhoneLookup.java
index c5d4e53f2..1642f9b23 100644
--- a/java/com/android/dialer/phonelookup/cp2/Cp2DefaultDirectoryPhoneLookup.java
+++ b/java/com/android/dialer/phonelookup/cp2/Cp2DefaultDirectoryPhoneLookup.java
@@ -644,6 +644,11 @@ public final class Cp2DefaultDirectoryPhoneLookup implements PhoneLookup<Cp2Info
});
}
+ @Override
+ public String getLoggingName() {
+ return "Cp2DefaultDirectoryPhoneLookup";
+ }
+
/**
* 1. get all contact ids. if the id is unset, add the number to the list of contacts to look up.
* 2. reduce our list of contact ids to those that were updated after lastModified. 3. Now we have
diff --git a/java/com/android/dialer/phonelookup/cp2/Cp2ExtendedDirectoryPhoneLookup.java b/java/com/android/dialer/phonelookup/cp2/Cp2ExtendedDirectoryPhoneLookup.java
index 2b98f265c..77a95e79f 100644
--- a/java/com/android/dialer/phonelookup/cp2/Cp2ExtendedDirectoryPhoneLookup.java
+++ b/java/com/android/dialer/phonelookup/cp2/Cp2ExtendedDirectoryPhoneLookup.java
@@ -234,4 +234,9 @@ public final class Cp2ExtendedDirectoryPhoneLookup implements PhoneLookup<Cp2Inf
public ListenableFuture<Void> clearData() {
return Futures.immediateFuture(null);
}
+
+ @Override
+ public String getLoggingName() {
+ return "Cp2ExtendedDirectoryPhoneLookup";
+ }
}
diff --git a/java/com/android/dialer/phonelookup/emergency/EmergencyPhoneLookup.java b/java/com/android/dialer/phonelookup/emergency/EmergencyPhoneLookup.java
new file mode 100644
index 000000000..d31614a12
--- /dev/null
+++ b/java/com/android/dialer/phonelookup/emergency/EmergencyPhoneLookup.java
@@ -0,0 +1,122 @@
+/*
+ * Copyright (C) 2018 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.phonelookup.emergency;
+
+import android.content.Context;
+import com.android.dialer.DialerPhoneNumber;
+import com.android.dialer.common.concurrent.Annotations.BackgroundExecutor;
+import com.android.dialer.inject.ApplicationContext;
+import com.android.dialer.phonelookup.PhoneLookup;
+import com.android.dialer.phonelookup.PhoneLookupInfo;
+import com.android.dialer.phonelookup.PhoneLookupInfo.EmergencyInfo;
+import com.android.dialer.phonenumberutil.PhoneNumberHelper;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.util.concurrent.Futures;
+import com.google.common.util.concurrent.ListenableFuture;
+import com.google.common.util.concurrent.ListeningExecutorService;
+import javax.inject.Inject;
+
+/**
+ * PhoneLookup implementation for checking if a number is an emergency number.
+ *
+ * <p>The check has to be done in a PhoneLookup as it involves detecting the user's location and
+ * obtaining SIM info, which are expensive operations. Doing it in the main thread will make the UI
+ * super janky.
+ */
+public class EmergencyPhoneLookup implements PhoneLookup<EmergencyInfo> {
+
+ private final Context appContext;
+ private final ListeningExecutorService backgroundExecutorService;
+
+ @Inject
+ EmergencyPhoneLookup(
+ @ApplicationContext Context appContext,
+ @BackgroundExecutor ListeningExecutorService backgroundExecutorService) {
+ this.appContext = appContext;
+ this.backgroundExecutorService = backgroundExecutorService;
+ }
+
+ @Override
+ public ListenableFuture<EmergencyInfo> lookup(DialerPhoneNumber dialerPhoneNumber) {
+ return backgroundExecutorService.submit(
+ () ->
+ EmergencyInfo.newBuilder()
+ .setIsEmergencyNumber(
+ PhoneNumberHelper.isLocalEmergencyNumber(
+ appContext, dialerPhoneNumber.getNormalizedNumber()))
+ .build());
+ }
+
+ @Override
+ public ListenableFuture<Boolean> isDirty(ImmutableSet<DialerPhoneNumber> phoneNumbers) {
+ return Futures.immediateFuture(false);
+ }
+
+ @Override
+ public ListenableFuture<ImmutableMap<DialerPhoneNumber, EmergencyInfo>> getMostRecentInfo(
+ ImmutableMap<DialerPhoneNumber, EmergencyInfo> existingInfoMap) {
+ // We can update EmergencyInfo for all numbers in the provided map, but the negative impact on
+ // performance is intolerable as checking a single number involves detecting the user's location
+ // and obtaining SIM info, which will take more than 100ms (see
+ // android.telephony.PhoneNumberUtils#isLocalEmergencyNumber(Context, int, String) for details).
+ //
+ // As emergency numbers won't change in a country, the only case we will miss is that
+ // (1) a number is an emergency number in country A but not in country B,
+ // (2) a user has an emergency call entry when they are in country A, and
+ // (3) they travel from A to B,
+ // which is a rare event.
+ //
+ // We can update the implementation if telecom supports batch check in the future.
+ return Futures.immediateFuture(existingInfoMap);
+ }
+
+ @Override
+ public void setSubMessage(PhoneLookupInfo.Builder destination, EmergencyInfo subMessage) {
+ destination.setEmergencyInfo(subMessage);
+ }
+
+ @Override
+ public EmergencyInfo getSubMessage(PhoneLookupInfo phoneLookupInfo) {
+ return phoneLookupInfo.getEmergencyInfo();
+ }
+
+ @Override
+ public ListenableFuture<Void> onSuccessfulBulkUpdate() {
+ return Futures.immediateFuture(null);
+ }
+
+ @Override
+ public void registerContentObservers() {
+ // No content observer to register.
+ }
+
+ @Override
+ public void unregisterContentObservers() {
+ // Nothing to be done as no content observer is registered.
+ }
+
+ @Override
+ public ListenableFuture<Void> clearData() {
+ return Futures.immediateFuture(null);
+ }
+
+ @Override
+ public String getLoggingName() {
+ return "EmergencyPhoneLookup";
+ }
+}
diff --git a/java/com/android/dialer/phonelookup/phone_lookup_info.proto b/java/com/android/dialer/phonelookup/phone_lookup_info.proto
index 50817c4e3..9e9dfa918 100644
--- a/java/com/android/dialer/phonelookup/phone_lookup_info.proto
+++ b/java/com/android/dialer/phonelookup/phone_lookup_info.proto
@@ -14,7 +14,7 @@ package com.android.dialer.phonelookup;
// "cp2_info_in_default_directory" corresponds to class
// Cp2DefaultDirectoryPhoneLookup, and class Cp2DefaultDirectoryPhoneLookup
// alone is responsible for populating it.
-// Next ID: 9
+// Next ID: 10
message PhoneLookupInfo {
// Information about a PhoneNumber retrieved from CP2.
message Cp2Info {
@@ -175,4 +175,11 @@ message PhoneLookupInfo {
optional string photo_uri = 3;
}
optional CequintInfo cequint_info = 8;
+
+ // Message indicating whether a number is an emergency number.
+ // Next ID: 2
+ message EmergencyInfo {
+ optional bool is_emergency_number = 1;
+ }
+ optional EmergencyInfo emergency_info = 9;
} \ No newline at end of file
diff --git a/java/com/android/dialer/phonelookup/spam/SpamPhoneLookup.java b/java/com/android/dialer/phonelookup/spam/SpamPhoneLookup.java
index 6b77036cf..7e5c9734d 100644
--- a/java/com/android/dialer/phonelookup/spam/SpamPhoneLookup.java
+++ b/java/com/android/dialer/phonelookup/spam/SpamPhoneLookup.java
@@ -27,7 +27,7 @@ import com.android.dialer.phonelookup.PhoneLookup;
import com.android.dialer.phonelookup.PhoneLookupInfo;
import com.android.dialer.phonelookup.PhoneLookupInfo.SpamInfo;
import com.android.dialer.spam.Spam;
-import com.android.dialer.spam.SpamStatus;
+import com.android.dialer.spam.status.SpamStatus;
import com.android.dialer.storage.Unencrypted;
import com.google.common.base.Optional;
import com.google.common.collect.ImmutableMap;
@@ -167,4 +167,9 @@ public final class SpamPhoneLookup implements PhoneLookup<SpamInfo> {
return null;
});
}
+
+ @Override
+ public String getLoggingName() {
+ return "SpamPhoneLookup";
+ }
}
diff --git a/java/com/android/dialer/precall/PreCallCoordinator.java b/java/com/android/dialer/precall/PreCallCoordinator.java
index 9c24e0d69..4d4859a80 100644
--- a/java/com/android/dialer/precall/PreCallCoordinator.java
+++ b/java/com/android/dialer/precall/PreCallCoordinator.java
@@ -68,8 +68,8 @@ public interface PreCallCoordinator {
@NonNull
PendingAction startPendingAction();
- <Output> void listen(
- ListenableFuture<Output> future,
- Consumer<Output> successListener,
+ <OutputT> void listen(
+ ListenableFuture<OutputT> future,
+ Consumer<OutputT> successListener,
Consumer<Throwable> failureListener);
}
diff --git a/java/com/android/dialer/precall/impl/DuoAction.java b/java/com/android/dialer/precall/impl/DuoAction.java
new file mode 100644
index 000000000..c05fbe12f
--- /dev/null
+++ b/java/com/android/dialer/precall/impl/DuoAction.java
@@ -0,0 +1,97 @@
+/*
+ * Copyright (C) 2018 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.precall.impl;
+
+import android.content.Context;
+import android.content.Intent;
+import com.android.dialer.callintent.CallIntentBuilder;
+import com.android.dialer.common.LogUtil;
+import com.android.dialer.common.concurrent.Annotations.Ui;
+import com.android.dialer.duo.Duo.ReachabilityData;
+import com.android.dialer.duo.DuoComponent;
+import com.android.dialer.precall.PreCallAction;
+import com.android.dialer.precall.PreCallCoordinator;
+import com.android.dialer.precall.PreCallCoordinator.PendingAction;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.util.concurrent.FutureCallback;
+import com.google.common.util.concurrent.Futures;
+import com.google.common.util.concurrent.ListenableFuture;
+import com.google.common.util.concurrent.ListeningExecutorService;
+import javax.inject.Inject;
+
+/**
+ * Checks if a duo call is actually callable, and request an activity for {@link
+ * android.app.Activity#startActivityForResult(Intent, int)}
+ */
+public class DuoAction implements PreCallAction {
+
+ private final ListeningExecutorService uiExecutor;
+
+ @Inject
+ DuoAction(@Ui ListeningExecutorService uiExecutor) {
+ this.uiExecutor = uiExecutor;
+ }
+
+ @Override
+ public boolean requiresUi(Context context, CallIntentBuilder builder) {
+ // Duo call must be started with startActivityForResult() which needs a activity.
+ return builder.isDuoCall();
+ }
+
+ @Override
+ public void runWithoutUi(Context context, CallIntentBuilder builder) {}
+
+ @Override
+ public void runWithUi(PreCallCoordinator coordinator) {
+ if (!requiresUi(coordinator.getActivity(), coordinator.getBuilder())) {
+ return;
+ }
+ String number = coordinator.getBuilder().getUri().getSchemeSpecificPart();
+ ListenableFuture<ImmutableMap<String, ReachabilityData>> reachabilities =
+ DuoComponent.get(coordinator.getActivity())
+ .getDuo()
+ .updateReachability(coordinator.getActivity(), ImmutableList.of(number));
+ PendingAction pendingAction = coordinator.startPendingAction();
+
+ Futures.addCallback(
+ reachabilities,
+ new FutureCallback<ImmutableMap<String, ReachabilityData>>() {
+ @Override
+ public void onSuccess(ImmutableMap<String, ReachabilityData> result) {
+ if (!result.containsKey(number) || !result.get(number).videoCallable()) {
+ LogUtil.w(
+ "DuoAction.runWithUi",
+ number + " number no longer duo reachable, falling back to carrier video call");
+ coordinator.getBuilder().setIsDuoCall(false);
+ }
+ pendingAction.finish();
+ }
+
+ @Override
+ public void onFailure(Throwable throwable) {
+ LogUtil.e("DuoAction.runWithUi", "reachability query failed", throwable);
+ coordinator.getBuilder().setIsDuoCall(false);
+ pendingAction.finish();
+ }
+ },
+ uiExecutor);
+ }
+
+ @Override
+ public void onDiscard() {}
+}
diff --git a/java/com/android/dialer/precall/impl/PreCallCoordinatorImpl.java b/java/com/android/dialer/precall/impl/PreCallCoordinatorImpl.java
index f2ff0e3e3..240549ca5 100644
--- a/java/com/android/dialer/precall/impl/PreCallCoordinatorImpl.java
+++ b/java/com/android/dialer/precall/impl/PreCallCoordinatorImpl.java
@@ -26,6 +26,7 @@ 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.UiListener;
+import com.android.dialer.duo.DuoComponent;
import com.android.dialer.function.Consumer;
import com.android.dialer.logging.DialerImpression.Type;
import com.android.dialer.logging.Logger;
@@ -33,6 +34,7 @@ import com.android.dialer.precall.PreCallAction;
import com.android.dialer.precall.PreCallComponent;
import com.android.dialer.precall.PreCallCoordinator;
import com.android.dialer.telecom.TelecomUtil;
+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.ListenableFuture;
@@ -101,7 +103,7 @@ public class PreCallCoordinatorImpl implements PreCallCoordinator {
LogUtil.enterBlock("PreCallCoordinatorImpl.runNextAction");
Assert.checkArgument(currentAction == null);
if (currentActionIndex >= actions.size()) {
- TelecomUtil.placeCall(activity, builder.build());
+ placeCall();
activity.finish();
return;
}
@@ -177,4 +179,20 @@ public class PreCallCoordinatorImpl implements PreCallCoordinator {
output -> successListener.accept((OutputT) output),
failureListener::accept);
}
+
+ private void placeCall() {
+ if (builder.isDuoCall()) {
+ Optional<Intent> intent =
+ DuoComponent.get(activity)
+ .getDuo()
+ .getCallIntent(builder.getUri().getSchemeSpecificPart());
+ if (intent.isPresent()) {
+ activity.startActivityForResult(intent.get(), 0);
+ return;
+ } else {
+ LogUtil.e("PreCallCoordinatorImpl.placeCall", "duo.getCallIntent() returned absent");
+ }
+ }
+ TelecomUtil.placeCall(activity, builder.build());
+ }
}
diff --git a/java/com/android/dialer/precall/impl/PreCallModule.java b/java/com/android/dialer/precall/impl/PreCallModule.java
index 9820e2b66..455453ef3 100644
--- a/java/com/android/dialer/precall/impl/PreCallModule.java
+++ b/java/com/android/dialer/precall/impl/PreCallModule.java
@@ -37,12 +37,13 @@ public abstract class PreCallModule {
@Provides
@Singleton
public static ImmutableList<PreCallAction> provideActions(
- CallingAccountSelector callingAccountSelector) {
+ DuoAction duoAction, CallingAccountSelector callingAccountSelector) {
return ImmutableList.of(
new PermissionCheckAction(),
new MalformedNumberRectifier(
ImmutableList.of(new UkRegionPrefixInInternationalFormatHandler())),
callingAccountSelector,
+ duoAction,
new AssistedDialAction());
}
}
diff --git a/java/com/android/dialer/searchfragment/list/NewSearchFragment.java b/java/com/android/dialer/searchfragment/list/NewSearchFragment.java
index f1eed91ba..abb3aecd4 100644
--- a/java/com/android/dialer/searchfragment/list/NewSearchFragment.java
+++ b/java/com/android/dialer/searchfragment/list/NewSearchFragment.java
@@ -51,9 +51,7 @@ import com.android.dialer.common.Assert;
import com.android.dialer.common.FragmentUtils;
import com.android.dialer.common.LogUtil;
import com.android.dialer.common.concurrent.ThreadUtil;
-import com.android.dialer.constants.ActivityRequestCodes;
import com.android.dialer.dialercontact.DialerContact;
-import com.android.dialer.duo.DuoComponent;
import com.android.dialer.enrichedcall.EnrichedCallComponent;
import com.android.dialer.enrichedcall.EnrichedCallManager.CapabilitiesListener;
import com.android.dialer.logging.DialerImpression;
@@ -549,8 +547,11 @@ public final class NewSearchFragment extends Fragment
public void placeDuoCall(String phoneNumber) {
Logger.get(getContext())
.logImpression(DialerImpression.Type.LIGHTBRINGER_VIDEO_REQUESTED_FROM_SEARCH);
- Intent intent = DuoComponent.get(getContext()).getDuo().getCallIntent(phoneNumber).orNull();
- getActivity().startActivityForResult(intent, ActivityRequestCodes.DIALTACTS_DUO);
+ PreCall.start(
+ getContext(),
+ new CallIntentBuilder(phoneNumber, CallInitiationType.Type.REGULAR_SEARCH)
+ .setIsVideoCall(true)
+ .setIsDuoCall(true));
FragmentUtils.getParentUnsafe(this, SearchFragmentListener.class).onCallPlacedFromSearch();
}
diff --git a/java/com/android/dialer/spam/Spam.java b/java/com/android/dialer/spam/Spam.java
index 21d770ed3..181a55dea 100644
--- a/java/com/android/dialer/spam/Spam.java
+++ b/java/com/android/dialer/spam/Spam.java
@@ -24,6 +24,7 @@ import com.android.dialer.DialerPhoneNumber;
import com.android.dialer.logging.ContactLookupResult;
import com.android.dialer.logging.ContactSource;
import com.android.dialer.logging.ReportingLocation;
+import com.android.dialer.spam.status.SpamStatus;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.common.util.concurrent.ListenableFuture;
@@ -41,6 +42,25 @@ public interface Spam {
ImmutableSet<DialerPhoneNumber> dialerPhoneNumbers);
/**
+ * Checks if the given number is suspected of being spam.
+ *
+ * @param dialerPhoneNumber the phone number.
+ * @return the {@link SpamStatus} for the given number.
+ */
+ ListenableFuture<SpamStatus> checkSpamStatus(DialerPhoneNumber dialerPhoneNumber);
+
+ /**
+ * Checks if the given number is suspected of being spam.
+ *
+ * <p>See {@link #checkSpamStatus(DialerPhoneNumber)}.
+ *
+ * @param number the phone number.
+ * @param defaultCountryIso the default country to use if it's not part of the number.
+ * @return the {@link SpamStatus} for the given number.
+ */
+ ListenableFuture<SpamStatus> checkSpamStatus(String number, @Nullable String defaultCountryIso);
+
+ /**
* Called as an indication that the Spam implementation should check whether downloading a spam
* list needs to occur or not.
*
@@ -55,15 +75,6 @@ public interface Spam {
ListenableFuture<Void> updateSpamListDownload(boolean isEnabledByUser);
/**
- * Checks if the given number is suspected of being a spam.
- *
- * @param number The phone number of the call.
- * @param countryIso The country ISO of the call.
- * @param listener The callback to be invoked after {@code Info} is fetched.
- */
- void checkSpamStatus(String number, String countryIso, Listener listener);
-
- /**
* @param number The number to check if the number is in the user's white list (non spam list)
* @param countryIso The country ISO of the call.
* @param listener The callback to be invoked after {@code Info} is fetched.
diff --git a/java/com/android/dialer/spam/SpamComponent.java b/java/com/android/dialer/spam/SpamComponent.java
index 2b70b6fe1..a0ffcce1f 100644
--- a/java/com/android/dialer/spam/SpamComponent.java
+++ b/java/com/android/dialer/spam/SpamComponent.java
@@ -18,6 +18,7 @@ package com.android.dialer.spam;
import android.content.Context;
import com.android.dialer.inject.HasRootComponent;
+import com.android.dialer.inject.IncludeInDialerRoot;
import dagger.Subcomponent;
/** Dagger component to get Spam. */
@@ -35,6 +36,7 @@ public abstract class SpamComponent {
}
/** Used to refer to the root application component. */
+ @IncludeInDialerRoot
public interface HasComponent {
SpamComponent spamComponent();
}
diff --git a/java/com/android/dialer/spam/SpamStub.java b/java/com/android/dialer/spam/SpamStub.java
index 8851fd09e..2789c01e7 100644
--- a/java/com/android/dialer/spam/SpamStub.java
+++ b/java/com/android/dialer/spam/SpamStub.java
@@ -16,12 +16,14 @@
package com.android.dialer.spam;
+import android.support.annotation.Nullable;
import com.android.dialer.DialerPhoneNumber;
import com.android.dialer.common.concurrent.Annotations.BackgroundExecutor;
import com.android.dialer.logging.ContactLookupResult;
import com.android.dialer.logging.ContactSource;
import com.android.dialer.logging.ReportingLocation;
-import com.google.common.base.Optional;
+import com.android.dialer.spam.status.SimpleSpamStatus;
+import com.android.dialer.spam.status.SpamStatus;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.common.util.concurrent.Futures;
@@ -47,33 +49,27 @@ public class SpamStub implements Spam {
ImmutableMap.Builder<DialerPhoneNumber, SpamStatus> resultBuilder =
new ImmutableMap.Builder<>();
for (DialerPhoneNumber dialerPhoneNumber : dialerPhoneNumbers) {
- resultBuilder.put(
- dialerPhoneNumber,
- new SpamStatus() {
- @Override
- public boolean isSpam() {
- return false;
- }
-
- @Override
- public Optional<Long> getTimestampMillis() {
- return Optional.absent();
- }
- });
+ resultBuilder.put(dialerPhoneNumber, SimpleSpamStatus.notSpam());
}
return resultBuilder.build();
});
}
@Override
- public ListenableFuture<Void> updateSpamListDownload(boolean isEnabledByUser) {
- // no-op
- return Futures.immediateFuture(null);
+ public ListenableFuture<SpamStatus> checkSpamStatus(DialerPhoneNumber dialerPhoneNumber) {
+ return Futures.immediateFuture(SimpleSpamStatus.notSpam());
}
@Override
- public void checkSpamStatus(String number, String countryIso, Listener listener) {
- listener.onComplete(false);
+ public ListenableFuture<SpamStatus> checkSpamStatus(
+ String number, @Nullable String defaultCountryIso) {
+ return Futures.immediateFuture(SimpleSpamStatus.notSpam());
+ }
+
+ @Override
+ public ListenableFuture<Void> updateSpamListDownload(boolean isEnabledByUser) {
+ // no-op
+ return Futures.immediateFuture(null);
}
@Override
diff --git a/java/com/android/dialer/spam/StubSpamModule.java b/java/com/android/dialer/spam/StubSpamModule.java
index 5540408ad..b60967475 100644
--- a/java/com/android/dialer/spam/StubSpamModule.java
+++ b/java/com/android/dialer/spam/StubSpamModule.java
@@ -16,10 +16,13 @@
package com.android.dialer.spam;
+import com.android.dialer.inject.DialerVariant;
+import com.android.dialer.inject.InstallIn;
import dagger.Binds;
import dagger.Module;
/** Module which binds {@link SpamStub}. */
+@InstallIn(variants = {DialerVariant.DIALER_TEST})
@Module
public abstract class StubSpamModule {
diff --git a/java/com/android/dialer/spam/promo/SpamBlockingPromoHelper.java b/java/com/android/dialer/spam/promo/SpamBlockingPromoHelper.java
index a117e1908..42fb39f38 100644
--- a/java/com/android/dialer/spam/promo/SpamBlockingPromoHelper.java
+++ b/java/com/android/dialer/spam/promo/SpamBlockingPromoHelper.java
@@ -42,6 +42,8 @@ public class SpamBlockingPromoHelper {
static final String SPAM_BLOCKING_PROMO_PERIOD_MILLIS = "spam_blocking_promo_period_millis";
static final String SPAM_BLOCKING_PROMO_LAST_SHOW_MILLIS = "spam_blocking_promo_last_show_millis";
public static final String ENABLE_SPAM_BLOCKING_PROMO = "enable_spam_blocking_promo";
+ public static final String ENABLE_AFTER_CALL_SPAM_BLOCKING_PROMO =
+ "enable_after_call_spam_blocking_promo";
private final Context context;
private final SpamSettings spamSettings;
@@ -77,6 +79,13 @@ public class SpamBlockingPromoHelper {
return lastShowMillis == 0 || System.currentTimeMillis() - lastShowMillis > showPeriodMillis;
}
+ /* Returns true if we should show a spam blocking promo in after call notification scenario. */
+ public boolean shouldShowAfterCallSpamBlockingPromo() {
+ return shouldShowSpamBlockingPromo()
+ && ConfigProviderBindings.get(context)
+ .getBoolean(ENABLE_AFTER_CALL_SPAM_BLOCKING_PROMO, false);
+ }
+
/**
* Shows a spam blocking promo dialog.
*
diff --git a/java/com/android/dialer/spam/status/GlobalSpamListStatus.java b/java/com/android/dialer/spam/status/GlobalSpamListStatus.java
new file mode 100644
index 000000000..741d6e869
--- /dev/null
+++ b/java/com/android/dialer/spam/status/GlobalSpamListStatus.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2018 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.spam.status;
+
+import android.support.annotation.IntDef;
+import com.google.auto.value.AutoValue;
+import com.google.common.base.Optional;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/** A value class representing a number's spam status in the global spam list. */
+@AutoValue
+public abstract class GlobalSpamListStatus {
+
+ /** Integers representing the spam status in the global spam list. */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef({Status.NOT_ON_LIST, Status.ON_LIST})
+ public @interface Status {
+ int NOT_ON_LIST = 1;
+ int ON_LIST = 2;
+ }
+
+ public abstract @Status int getStatus();
+
+ /**
+ * Returns the timestamp (in milliseconds) representing when a number's spam status was put on the
+ * list, or {@code Optional.absent()} if the number is not on the list.
+ */
+ public abstract Optional<Long> getTimestampMillis();
+
+ public static GlobalSpamListStatus notOnList() {
+ return new AutoValue_GlobalSpamListStatus(Status.NOT_ON_LIST, Optional.absent());
+ }
+
+ public static GlobalSpamListStatus onList(long timestampMillis) {
+ return new AutoValue_GlobalSpamListStatus(Status.ON_LIST, Optional.of(timestampMillis));
+ }
+}
diff --git a/java/com/android/dialer/spam/status/SimpleSpamStatus.java b/java/com/android/dialer/spam/status/SimpleSpamStatus.java
new file mode 100644
index 000000000..ec28b9dce
--- /dev/null
+++ b/java/com/android/dialer/spam/status/SimpleSpamStatus.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2018 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.spam.status;
+
+import android.support.annotation.Nullable;
+import com.google.auto.value.AutoValue;
+import com.google.common.base.Optional;
+
+/** Holds a boolean and long to represent spam status. */
+@AutoValue
+public abstract class SimpleSpamStatus implements SpamStatus {
+
+ /** Returns a SimpleSpamStatus with the given boolean and timestamp. */
+ public static SimpleSpamStatus create(boolean isSpam, @Nullable Long timestampMillis) {
+ return new AutoValue_SimpleSpamStatus(isSpam, Optional.fromNullable(timestampMillis));
+ }
+
+ /** Returns a SimpleSpamStatus that's not marked as spam and has no timestamp. */
+ public static SimpleSpamStatus notSpam() {
+ return create(false, null);
+ }
+}
diff --git a/java/com/android/dialer/spam/SpamStatus.java b/java/com/android/dialer/spam/status/SpamStatus.java
index 0b859d1c7..8186ac5cc 100644
--- a/java/com/android/dialer/spam/SpamStatus.java
+++ b/java/com/android/dialer/spam/status/SpamStatus.java
@@ -14,7 +14,7 @@
* limitations under the License
*/
-package com.android.dialer.spam;
+package com.android.dialer.spam.status;
import com.google.common.base.Optional;
diff --git a/java/com/android/dialer/spam/status/UserSpamListStatus.java b/java/com/android/dialer/spam/status/UserSpamListStatus.java
new file mode 100644
index 000000000..01f99872b
--- /dev/null
+++ b/java/com/android/dialer/spam/status/UserSpamListStatus.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2018 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.spam.status;
+
+import android.support.annotation.IntDef;
+import com.google.auto.value.AutoValue;
+import com.google.common.base.Optional;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/** A value class representing a number's spam status in the user spam list. */
+@AutoValue
+@SuppressWarnings("Guava")
+public abstract class UserSpamListStatus {
+
+ /** Integers representing the spam status in the user spam list. */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef({Status.NOT_ON_LIST, Status.WHITELISTED, Status.BLACKLISTED})
+ public @interface Status {
+ int NOT_ON_LIST = 1;
+ int WHITELISTED = 2;
+ int BLACKLISTED = 3;
+ }
+
+ public abstract @Status int getStatus();
+
+ /**
+ * Returns the timestamp (in milliseconds) representing when a number's spam status was put on the
+ * list, or {@code Optional.absent()} if the number is not on the list.
+ */
+ public abstract Optional<Long> getTimestampMillis();
+
+ public static UserSpamListStatus notOnList() {
+ return new AutoValue_UserSpamListStatus(Status.NOT_ON_LIST, Optional.absent());
+ }
+
+ public static UserSpamListStatus whitelisted(long timestampMillis) {
+ return new AutoValue_UserSpamListStatus(Status.WHITELISTED, Optional.of(timestampMillis));
+ }
+
+ public static UserSpamListStatus blacklisted(long timestampMillis) {
+ return new AutoValue_UserSpamListStatus(Status.BLACKLISTED, Optional.of(timestampMillis));
+ }
+}
diff --git a/java/com/android/dialer/speeddial/DisambigDialog.java b/java/com/android/dialer/speeddial/DisambigDialog.java
index 98fc992f8..2dd43fa0a 100644
--- a/java/com/android/dialer/speeddial/DisambigDialog.java
+++ b/java/com/android/dialer/speeddial/DisambigDialog.java
@@ -18,7 +18,6 @@ package com.android.dialer.speeddial;
import android.app.Dialog;
import android.content.Context;
-import android.content.Intent;
import android.os.Bundle;
import android.support.annotation.VisibleForTesting;
import android.support.annotation.WorkerThread;
@@ -40,8 +39,6 @@ import com.android.dialer.common.Assert;
import com.android.dialer.common.LogUtil;
import com.android.dialer.common.concurrent.DefaultFutureCallback;
import com.android.dialer.common.concurrent.DialerExecutorComponent;
-import com.android.dialer.constants.ActivityRequestCodes;
-import com.android.dialer.duo.DuoComponent;
import com.android.dialer.logging.DialerImpression;
import com.android.dialer.logging.Logger;
import com.android.dialer.precall.PreCall;
@@ -184,16 +181,13 @@ public class DisambigDialog extends DialogFragment {
Logger.get(getContext())
.logImpression(
DialerImpression.Type.LIGHTBRINGER_VIDEO_REQUESTED_FOR_FAVORITE_CONTACT_DISAMBIG);
- Intent intent =
- DuoComponent.get(getContext()).getDuo().getCallIntent(channel.number()).orNull();
- getActivity().startActivityForResult(intent, ActivityRequestCodes.DIALTACTS_DUO);
- return;
}
PreCall.start(
getContext(),
new CallIntentBuilder(channel.number(), CallInitiationType.Type.SPEED_DIAL)
- .setIsVideoCall(true));
+ .setIsVideoCall(true)
+ .setIsDuoCall(channel.technology() == Channel.DUO));
dismiss();
}
diff --git a/java/com/android/dialer/speeddial/SpeedDialFragment.java b/java/com/android/dialer/speeddial/SpeedDialFragment.java
index aa306d214..fac9a13d2 100644
--- a/java/com/android/dialer/speeddial/SpeedDialFragment.java
+++ b/java/com/android/dialer/speeddial/SpeedDialFragment.java
@@ -41,7 +41,6 @@ import com.android.dialer.common.concurrent.DefaultFutureCallback;
import com.android.dialer.common.concurrent.DialerExecutorComponent;
import com.android.dialer.common.concurrent.SupportUiListener;
import com.android.dialer.constants.ActivityRequestCodes;
-import com.android.dialer.duo.DuoComponent;
import com.android.dialer.historyitemactions.DividerModule;
import com.android.dialer.historyitemactions.HistoryItemActionBottomSheet;
import com.android.dialer.historyitemactions.HistoryItemActionModule;
@@ -243,16 +242,13 @@ public class SpeedDialFragment extends Fragment {
if (channel.technology() == Channel.DUO) {
Logger.get(activity)
.logImpression(DialerImpression.Type.LIGHTBRINGER_VIDEO_REQUESTED_FOR_FAVORITE_CONTACT);
- Intent intent =
- DuoComponent.get(activity).getDuo().getCallIntent(channel.number()).orNull();
- activity.startActivityForResult(intent, ActivityRequestCodes.DIALTACTS_DUO);
- return;
}
PreCall.start(
activity,
new CallIntentBuilder(channel.number(), CallInitiationType.Type.SPEED_DIAL)
- .setIsVideoCall(channel.isVideoTechnology()));
+ .setIsVideoCall(channel.isVideoTechnology())
+ .setIsDuoCall(channel.technology() == Channel.DUO));
}
@Override
@@ -341,15 +337,12 @@ public class SpeedDialFragment extends Fragment {
Logger.get(getContext())
.logImpression(
DialerImpression.Type.LIGHTBRINGER_VIDEO_REQUESTED_FOR_SUGGESTED_CONTACT);
- Intent intent =
- DuoComponent.get(getContext()).getDuo().getCallIntent(channel.number()).orNull();
- getActivity().startActivityForResult(intent, ActivityRequestCodes.DIALTACTS_DUO);
- return;
}
PreCall.start(
getContext(),
new CallIntentBuilder(channel.number(), CallInitiationType.Type.SPEED_DIAL)
- .setIsVideoCall(channel.isVideoTechnology()));
+ .setIsVideoCall(channel.isVideoTechnology())
+ .setIsDuoCall(channel.technology() == Channel.DUO));
}
private final class StarContactModule implements HistoryItemActionModule {
@@ -426,15 +419,12 @@ public class SpeedDialFragment extends Fragment {
if (channel.technology() == Channel.DUO) {
Logger.get(activity)
.logImpression(DialerImpression.Type.LIGHTBRINGER_VIDEO_REQUESTED_FOR_FAVORITE_CONTACT);
- Intent intent =
- DuoComponent.get(activity).getDuo().getCallIntent(channel.number()).orNull();
- activity.startActivityForResult(intent, ActivityRequestCodes.DIALTACTS_DUO);
- return;
}
PreCall.start(
activity,
new CallIntentBuilder(channel.number(), CallInitiationType.Type.SPEED_DIAL)
- .setIsVideoCall(channel.isVideoTechnology()));
+ .setIsVideoCall(channel.isVideoTechnology())
+ .setIsDuoCall(channel.technology() == Channel.DUO));
}
@Override
diff --git a/java/com/android/dialer/storage/StorageComponent.java b/java/com/android/dialer/storage/StorageComponent.java
index cb5c4a879..4754cc6db 100644
--- a/java/com/android/dialer/storage/StorageComponent.java
+++ b/java/com/android/dialer/storage/StorageComponent.java
@@ -19,6 +19,7 @@ package com.android.dialer.storage;
import android.content.Context;
import android.content.SharedPreferences;
import com.android.dialer.inject.HasRootComponent;
+import com.android.dialer.inject.IncludeInDialerRoot;
import dagger.Subcomponent;
/** Dagger component for storage. */
@@ -42,6 +43,7 @@ public abstract class StorageComponent {
}
/** Used to refer to the root application component. */
+ @IncludeInDialerRoot
public interface HasComponent {
StorageComponent storageComponent();
}
diff --git a/java/com/android/dialer/storage/StorageModule.java b/java/com/android/dialer/storage/StorageModule.java
index e1c5b4b08..370998cdb 100644
--- a/java/com/android/dialer/storage/StorageModule.java
+++ b/java/com/android/dialer/storage/StorageModule.java
@@ -20,11 +20,14 @@ import android.content.SharedPreferences;
import android.preference.PreferenceManager;
import android.support.v4.content.ContextCompat;
import com.android.dialer.inject.ApplicationContext;
+import com.android.dialer.inject.DialerVariant;
+import com.android.dialer.inject.InstallIn;
import dagger.Module;
import dagger.Provides;
import javax.inject.Singleton;
/** Module for the storage component. */
+@InstallIn(variants = {DialerVariant.DIALER_TEST})
@Module
public class StorageModule {
diff --git a/java/com/android/dialer/voicemail/listui/menu/Modules.java b/java/com/android/dialer/voicemail/listui/menu/Modules.java
index dcd9116e9..5a9a7110c 100644
--- a/java/com/android/dialer/voicemail/listui/menu/Modules.java
+++ b/java/com/android/dialer/voicemail/listui/menu/Modules.java
@@ -58,6 +58,7 @@ final class Modules {
.setCanSupportCarrierVideoCall(
voicemailEntry.getNumberAttributes().getCanSupportCarrierVideoCall())
.setIsBlocked(voicemailEntry.getNumberAttributes().getIsBlocked())
+ .setIsEmergencyNumber(voicemailEntry.getNumberAttributes().getIsEmergencyNumber())
.setIsSpam(voicemailEntry.getNumberAttributes().getIsSpam())
// A voicemail call is an outgoing call to the voicemail box.
// Voicemail entries are not voicemail calls.
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;
diff --git a/java/com/android/voicemail/VoicemailComponent.java b/java/com/android/voicemail/VoicemailComponent.java
index bed75f0ef..0e09627b3 100644
--- a/java/com/android/voicemail/VoicemailComponent.java
+++ b/java/com/android/voicemail/VoicemailComponent.java
@@ -18,6 +18,7 @@ package com.android.voicemail;
import android.content.Context;
import com.android.dialer.inject.HasRootComponent;
+import com.android.dialer.inject.IncludeInDialerRoot;
import dagger.Subcomponent;
/** Subcomponent that can be used to access the voicemail implementation. */
@@ -32,6 +33,7 @@ public abstract class VoicemailComponent {
}
/** Used to refer to the root application component. */
+ @IncludeInDialerRoot
public interface HasComponent {
VoicemailComponent voicemailComponent();
}
diff --git a/java/com/android/voicemail/stub/StubVoicemailModule.java b/java/com/android/voicemail/stub/StubVoicemailModule.java
index 6c1552c15..efcb4cffa 100644
--- a/java/com/android/voicemail/stub/StubVoicemailModule.java
+++ b/java/com/android/voicemail/stub/StubVoicemailModule.java
@@ -16,6 +16,8 @@
package com.android.voicemail.stub;
+import com.android.dialer.inject.DialerVariant;
+import com.android.dialer.inject.InstallIn;
import com.android.voicemail.VoicemailClient;
import dagger.Binds;
import dagger.Module;
@@ -24,6 +26,7 @@ import javax.inject.Singleton;
/**
* A no-op version of the voicemail module for build targets that don't support the new OTMP client.
*/
+@InstallIn(variants = {DialerVariant.DIALER_TEST})
@Module
public abstract class StubVoicemailModule {