summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-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 {